From b06030f2bce0226bc83586ac74cc4b89b718841b Mon Sep 17 00:00:00 2001 From: Pascal de Bruijn Date: Tue, 23 Feb 2021 19:23:35 +0100 Subject: [PATCH 001/285] creality.ini: allow slightly steeper unsupported angles --- resources/profiles/Creality.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index 1426ee5963..e66df8f668 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -335,7 +335,7 @@ support_material_pattern = rectilinear support_material_spacing = 2 support_material_speed = 40 support_material_synchronize_layers = 0 -support_material_threshold = 45 +support_material_threshold = 40 support_material_with_sheath = 0 support_material_xy_spacing = 60% thin_walls = 0 From f9b4bbb016566eead32d32092939a88c10c80f1f Mon Sep 17 00:00:00 2001 From: Pascal de Bruijn Date: Tue, 23 Feb 2021 19:53:30 +0100 Subject: [PATCH 002/285] creality.ini: more accurate extruder_clearance for Ender-3 radius = 47mm = 45mm carriage (including bltouch upgrade) + 2mm safety margin height = 34mm = 30mm nozzle + 2mm bltouch probe pin + 2mm safety margin --- resources/profiles/Creality.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index e66df8f668..e09f829d8c 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -269,8 +269,8 @@ external_perimeters_first = 0 external_perimeter_extrusion_width = 0.45 external_perimeter_speed = 25 extra_perimeters = 0 -extruder_clearance_height = 25 -extruder_clearance_radius = 45 +extruder_clearance_height = 34 +extruder_clearance_radius = 47 extrusion_width = 0.45 fill_angle = 45 fill_density = 20% From 9b7621dc7e5c824b468342d4bad81b826b2e7e86 Mon Sep 17 00:00:00 2001 From: Pascal de Bruijn Date: Tue, 23 Feb 2021 20:06:55 +0100 Subject: [PATCH 003/285] creality.ini: double digit layer height (cosmetic) --- resources/profiles/Creality.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index e09f829d8c..9d86d3deab 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -359,7 +359,7 @@ top_solid_layers = 11 [print:*0.10mm*] inherits = *common* -layer_height = 0.1 +layer_height = 0.10 perimeters = 3 bottom_solid_layers = 7 top_solid_layers = 9 From 55c62887ccc2627ca714506a3221f9f1a9d7aef8 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 25 Feb 2021 20:23:53 +0100 Subject: [PATCH 004/285] Fix of #5503 - Physical printer disappears from Slicer upon close of application --- src/libslic3r/Preset.cpp | 27 ++++++++++++--------------- src/libslic3r/Preset.hpp | 4 ++-- src/libslic3r/PrintConfig.cpp | 10 ++++++---- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 6c500b84a6..98ccf280f2 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1393,9 +1393,8 @@ const std::vector& PhysicalPrinter::printer_options() static std::vector s_opts; if (s_opts.empty()) { s_opts = { - "preset_name", + "preset_names", "printer_technology", -// "printer_model", "host_type", "print_host", "printhost_apikey", @@ -1453,11 +1452,10 @@ bool PhysicalPrinter::has_empty_config() const void PhysicalPrinter::update_preset_names_in_config() { if (!preset_names.empty()) { - std::string name; - for (auto el : preset_names) - name += el + ";"; - name.pop_back(); - config.set_key_value("preset_name", new ConfigOptionString(name)); + std::vector& values = config.option("preset_names")->values; + values.clear(); + for (auto preset : preset_names) + values.push_back(preset); } } @@ -1482,14 +1480,13 @@ void PhysicalPrinter::update_from_config(const DynamicPrintConfig& new_config) { config.apply_only(new_config, printer_options(), false); - std::string str = config.opt_string("preset_name"); - std::set values{}; - if (!str.empty()) { - boost::split(values, str, boost::is_any_of(";")); + const std::vector& values = config.option("preset_names")->values; + + if (values.empty()) + preset_names.clear(); + else for (const std::string& val : values) preset_names.emplace(val); - } - preset_names = values; } void PhysicalPrinter::reset_presets() @@ -1817,7 +1814,7 @@ bool PhysicalPrinterCollection::delete_preset_from_printers( const std::string& return true; } -// Get list of printers which have more than one preset and "preset_name" preset is one of them +// Get list of printers which have more than one preset and "preset_names" preset is one of them std::vector PhysicalPrinterCollection::get_printers_with_preset(const std::string& preset_name) { std::vector printers; @@ -1832,7 +1829,7 @@ std::vector PhysicalPrinterCollection::get_printers_with_preset(con return printers; } -// Get list of printers which has only "preset_name" preset +// Get list of printers which has only "preset_names" preset std::vector PhysicalPrinterCollection::get_printers_with_only_preset(const std::string& preset_name) { std::vector printers; diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index d81717f0e0..6e56ad911d 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -685,9 +685,9 @@ public: // returns true if all presets were deleted successfully. bool delete_preset_from_printers(const std::string& preset_name); - // Get list of printers which have more than one preset and "preset_name" preset is one of them + // Get list of printers which have more than one preset and "preset_names" preset is one of them std::vector get_printers_with_preset( const std::string &preset_name); - // Get list of printers which has only "preset_name" preset + // Get list of printers which has only "preset_names" preset std::vector get_printers_with_only_preset( const std::string &preset_name); // Return the selected preset, without the user modifications applied. diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 480ce45901..810b651c40 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -151,11 +151,11 @@ void PrintConfigDef::init_common_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionString("")); - def = this->add("preset_name", coString); - def->label = L("Printer preset name"); - def->tooltip = L("Related printer preset name"); + def = this->add("preset_names", coStrings); + def->label = L("Printer preset names"); + def->tooltip = L("Names of presets related to the physical printer"); def->mode = comAdvanced; - def->set_default_value(new ConfigOptionString("")); + def->set_default_value(new ConfigOptionStrings()); def = this->add("printhost_authorization_type", coEnum); def->label = L("Authorization Type"); @@ -3366,6 +3366,8 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va opt_key = "printhost_cafile"; } else if (opt_key == "octoprint_apikey") { opt_key = "printhost_apikey"; + } else if (opt_key == "preset_name") { + opt_key = "preset_names"; } // Ignore the following obsolete configuration keys: From 0593a413503d41c6c14c1b47d6d56669556e8635 Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Thu, 25 Feb 2021 23:00:32 +0100 Subject: [PATCH 005/285] Updated vendor name. --- resources/profiles/INAT.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/profiles/INAT.ini b/resources/profiles/INAT.ini index 0c4cbe5f4d..e11aeab86f 100644 --- a/resources/profiles/INAT.ini +++ b/resources/profiles/INAT.ini @@ -2,7 +2,7 @@ [vendor] # Vendor name will be shown by the Config Wizard. -name = INAT s.r.o. +name = INAT config_version = 0.0.1 config_update_url = http://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/INAT/ From a53de51b2266137f687417292808a0e0c09b8cef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 25 Feb 2021 23:30:22 +0100 Subject: [PATCH 006/285] Fix of 8f293f0cb560b80b6ace68d1febdb026b379097f When was set use_external_mp_once to true then after first calling the avoid crossing perimeters this flag was reset which cases that on the second call of the avoid crossing perimeters the travel move didn't process as external. --- src/libslic3r/GCode.cpp | 15 +++++++++++---- src/libslic3r/GCode/AvoidCrossingPerimeters.hpp | 1 + 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 26a2062475..8384359610 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2838,9 +2838,11 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string Polyline travel { this->last_pos(), point }; // check whether a straight travel move would need retraction - bool needs_retraction = this->needs_retraction(travel, role); + bool needs_retraction = this->needs_retraction(travel, role); // check whether wipe could be disabled without causing visible stringing - bool could_be_wipe_disabled = false; + bool could_be_wipe_disabled = false; + // Save state of use_external_mp_once for the case that will be needed to call twice m_avoid_crossing_perimeters.travel_to. + const bool used_external_mp_once = m_avoid_crossing_perimeters.used_external_mp_once(); // if a retraction would be needed, try to use avoid_crossing_perimeters to plan a // multi-hop travel path inside the configuration space @@ -2868,8 +2870,13 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string // Because of it, it is necessary to call avoid crossing perimeters again with new starting point after calling retraction() // FIXME Lukas H.: Try to predict if this second calling of avoid crossing perimeters will be needed or not. It could save computations. if (last_post_before_retract != this->last_pos() && m_config.avoid_crossing_perimeters) { - Polyline retract_travel = m_avoid_crossing_perimeters.travel_to(*this, point); - travel = std::move(retract_travel); + // If in the previous call of m_avoid_crossing_perimeters.travel_to was use_external_mp_once set to true restore this value for next call. + if (used_external_mp_once) + m_avoid_crossing_perimeters.use_external_mp_once(); + travel = m_avoid_crossing_perimeters.travel_to(*this, point); + // If state of use_external_mp_once was changed reset it to right value. + if (used_external_mp_once) + m_avoid_crossing_perimeters.reset_once_modifiers(); } } else // Reset the wipe path when traveling, so one would not wipe along an old path. diff --git a/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp b/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp index 03c420a32b..d178e3c894 100644 --- a/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp +++ b/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp @@ -18,6 +18,7 @@ public: // Routing around the objects vs. inside a single object. void use_external_mp(bool use = true) { m_use_external_mp = use; }; void use_external_mp_once() { m_use_external_mp_once = true; } + bool used_external_mp_once() { return m_use_external_mp_once; } void disable_once() { m_disabled_once = true; } bool disabled_once() const { return m_disabled_once; } void reset_once_modifiers() { m_use_external_mp_once = false; m_disabled_once = false; } From 19cfeb1a78c695e6e0fa9c0d6e17452d9f751488 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 26 Feb 2021 07:57:48 +0100 Subject: [PATCH 007/285] Fix of a crash when using active_step_add_warning on PrintObjects PrintObjectBase::status_update_warnings called PrintBase::status_update_warnings, which in turn set SlicingStatus flag to UPDATE_PRINT_STEP_WARNINGS (instead of UPDATE_PRINT_OBEJCT_STEP_WARNINGS) and saved its own ObjectID. This led to spurious and hard to read thread-unsafe crashes. --- src/libslic3r/PrintBase.cpp | 12 +++++++----- src/libslic3r/PrintBase.hpp | 6 +++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/PrintBase.cpp b/src/libslic3r/PrintBase.cpp index 721741d4a8..d129aaad2b 100644 --- a/src/libslic3r/PrintBase.cpp +++ b/src/libslic3r/PrintBase.cpp @@ -94,12 +94,14 @@ std::string PrintBase::output_filepath(const std::string &path, const std::strin return path; } -void PrintBase::status_update_warnings(ObjectID object_id, int step, PrintStateBase::WarningLevel /* warning_level */, const std::string &message) +void PrintBase::status_update_warnings(int step, PrintStateBase::WarningLevel /* warning_level */, const std::string &message, const PrintObjectBase* print_object) { - if (this->m_status_callback) - m_status_callback(SlicingStatus(*this, step)); + if (this->m_status_callback) { + auto status = print_object ? SlicingStatus(*print_object, step) : SlicingStatus(*this, step); + m_status_callback(status); + } else if (! message.empty()) - printf("%s warning: %s\n", (object_id == this->id()) ? "print" : "print object", message.c_str()); + printf("%s warning: %s\n", print_object ? "print_object" : "print", message.c_str()); } tbb::mutex& PrintObjectBase::state_mutex(PrintBase *print) @@ -114,7 +116,7 @@ std::function PrintObjectBase::cancel_callback(PrintBase *print) void PrintObjectBase::status_update_warnings(PrintBase *print, int step, PrintStateBase::WarningLevel warning_level, const std::string &message) { - print->status_update_warnings(this->id(), step, warning_level, message); + print->status_update_warnings(step, warning_level, message, this); } } // namespace Slic3r diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp index 8fca43319a..2aff13ae9c 100644 --- a/src/libslic3r/PrintBase.hpp +++ b/src/libslic3r/PrintBase.hpp @@ -481,7 +481,7 @@ protected: // Notify UI about a new warning of a milestone "step" on this PrintBase. // The UI will be notified by calling a status callback. // If no status callback is registered, the message is printed to console. - void status_update_warnings(ObjectID object_id, int step, PrintStateBase::WarningLevel warning_level, const std::string &message); + void status_update_warnings(int step, PrintStateBase::WarningLevel warning_level, const std::string &message, const PrintObjectBase* print_object = nullptr); // If the background processing stop was requested, throw CanceledException. // To be called by the worker thread and its sub-threads (mostly launched on the TBB thread pool) regularly. @@ -528,7 +528,7 @@ protected: PrintStateBase::TimeStamp set_done(PrintStepEnum step) { std::pair status = m_state.set_done(step, this->state_mutex(), [this](){ this->throw_if_canceled(); }); if (status.second) - this->status_update_warnings(this->id(), static_cast(step), PrintStateBase::WarningLevel::NON_CRITICAL, std::string()); + this->status_update_warnings(static_cast(step), PrintStateBase::WarningLevel::NON_CRITICAL, std::string()); return status.first; } bool invalidate_step(PrintStepEnum step) @@ -550,7 +550,7 @@ protected: std::pair active_step = m_state.active_step_add_warning(warning_level, message, message_id, this->state_mutex()); if (active_step.second) // Update UI. - this->status_update_warnings(this->id(), static_cast(active_step.first), warning_level, message); + this->status_update_warnings(static_cast(active_step.first), warning_level, message); } private: From 8cb8a24dedaf682763f3be3a7f223edfd985a224 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 26 Feb 2021 10:18:16 +0100 Subject: [PATCH 008/285] Tech ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN set as default --- src/libslic3r/AppConfig.cpp | 4 ---- src/libslic3r/Technologies.hpp | 27 +++++++++---------------- src/slic3r/GUI/ConfigWizard.cpp | 8 -------- src/slic3r/GUI/ConfigWizard_private.hpp | 4 ---- src/slic3r/GUI/GUI_App.cpp | 10 --------- src/slic3r/GUI/GUI_App.hpp | 9 --------- src/slic3r/GUI/Preferences.cpp | 4 ---- 7 files changed, 10 insertions(+), 56 deletions(-) diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index 08e5fdf6d8..d9301d1f3d 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -68,14 +68,12 @@ void AppConfig::set_defaults() if (get("export_sources_full_pathnames").empty()) set("export_sources_full_pathnames", "0"); -#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN #ifdef _WIN32 if (get("associate_3mf").empty()) set("associate_3mf", "0"); if (get("associate_stl").empty()) set("associate_stl", "0"); #endif // _WIN32 -#endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN // remove old 'use_legacy_opengl' parameter from this config, if present if (!get("use_legacy_opengl").empty()) @@ -127,14 +125,12 @@ void AppConfig::set_defaults() if (get("color_mapinulation_panel").empty()) set("color_mapinulation_panel", "0"); } -#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN else { #ifdef _WIN32 if (get("associate_gcode").empty()) set("associate_gcode", "0"); #endif // _WIN32 } -#endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN if (get("seq_top_layer_only").empty()) set("seq_top_layer_only", "1"); diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 3ada1cd359..77e1712b15 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -4,7 +4,6 @@ //============= // debug techs //============= - // Shows camera target in the 3D scene #define ENABLE_SHOW_CAMERA_TARGET 0 // Log debug messages to console when changing selection @@ -27,26 +26,14 @@ #define ENABLE_GCODE_VIEWER_STATISTICS 0 // Enable G-Code viewer comparison between toolpaths height and width detected from gcode and calculated at gcode generation #define ENABLE_GCODE_VIEWER_DATA_CHECKING 0 + + // Enable rendering of objects using environment map #define ENABLE_ENVIRONMENT_MAP 0 // Enable smoothing of objects normals #define ENABLE_SMOOTH_NORMALS 0 - - -//==================== -// 2.3.0.alpha4 techs -//==================== -#define ENABLE_2_3_0_ALPHA4 1 - -#define ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS (1 && ENABLE_2_3_0_ALPHA4) - - -//=================== -// 2.3.0.beta1 techs -//=================== -#define ENABLE_2_3_0_BETA1 1 - -#define ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN (1 && ENABLE_2_3_0_BETA1) +// Enable rendering markers for options in preview as fixed screen size points +#define ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS 1 //==================== @@ -54,11 +41,17 @@ //==================== #define ENABLE_2_3_1_ALPHA1 1 +// Enable splitting of vertex buffers used to render toolpaths #define ENABLE_SPLITTED_VERTEX_BUFFER (1 && ENABLE_2_3_1_ALPHA1) +// Enable rendering only starting and final caps for toolpaths #define ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS (1 && ENABLE_SPLITTED_VERTEX_BUFFER) +// Enable reload from disk command for 3mf files #define ENABLE_RELOAD_FROM_DISK_FOR_3MF (1 && ENABLE_2_3_1_ALPHA1) +// Removes obsolete warning texture code #define ENABLE_WARNING_TEXTURE_REMOVAL (1 && ENABLE_2_3_1_ALPHA1) +// Enable showing gcode line numbers in previeww horizontal slider #define ENABLE_GCODE_LINES_ID_IN_H_SLIDER (1 && ENABLE_2_3_1_ALPHA1) +// Enable validation of custom gcode against gcode processor resserved keywords #define ENABLE_VALIDATE_CUSTOM_GCODE (1 && ENABLE_2_3_1_ALPHA1) diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index b0bb6a8a4a..77e8472016 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -1186,7 +1186,6 @@ PageReloadFromDisk::PageReloadFromDisk(ConfigWizard* parent) box_pathnames->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& event) { this->full_pathnames = event.IsChecked(); }); } -#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN #ifdef _WIN32 PageFilesAssociation::PageFilesAssociation(ConfigWizard* parent) : ConfigWizardPage(parent, _L("Files association"), _L("Files association")) @@ -1200,7 +1199,6 @@ PageFilesAssociation::PageFilesAssociation(ConfigWizard* parent) // append(cb_gcode); } #endif // _WIN32 -#endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN PageMode::PageMode(ConfigWizard *parent) : ConfigWizardPage(parent, _L("View mode"), _L("View mode")) @@ -1813,11 +1811,9 @@ void ConfigWizard::priv::load_pages() index->add_page(page_update); index->add_page(page_reload_from_disk); -#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN #ifdef _WIN32 index->add_page(page_files_association); #endif // _WIN32 -#endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN index->add_page(page_mode); index->go_to(former_active); // Will restore the active item/page if possible @@ -2411,7 +2407,6 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese app_config->set("preset_update", page_update->preset_update ? "1" : "0"); app_config->set("export_sources_full_pathnames", page_reload_from_disk->full_pathnames ? "1" : "0"); -#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN #ifdef _WIN32 app_config->set("associate_3mf", page_files_association->associate_3mf() ? "1" : "0"); app_config->set("associate_stl", page_files_association->associate_stl() ? "1" : "0"); @@ -2429,7 +2424,6 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese // } #endif // _WIN32 -#endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN page_mode->serialize_mode(app_config); @@ -2594,11 +2588,9 @@ ConfigWizard::ConfigWizard(wxWindow *parent) p->add_page(p->page_update = new PageUpdate(this)); p->add_page(p->page_reload_from_disk = new PageReloadFromDisk(this)); -#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN #ifdef _WIN32 p->add_page(p->page_files_association = new PageFilesAssociation(this)); #endif // _WIN32 -#endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN p->add_page(p->page_mode = new PageMode(this)); p->add_page(p->page_firmware = new PageFirmware(this)); p->add_page(p->page_bed = new PageBedShape(this)); diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp index 581ec39b18..eee906ae77 100644 --- a/src/slic3r/GUI/ConfigWizard_private.hpp +++ b/src/slic3r/GUI/ConfigWizard_private.hpp @@ -393,7 +393,6 @@ struct PageReloadFromDisk : ConfigWizardPage PageReloadFromDisk(ConfigWizard* parent); }; -#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN #ifdef _WIN32 struct PageFilesAssociation : ConfigWizardPage { @@ -410,7 +409,6 @@ public: // bool associate_gcode() const { return cb_gcode->IsChecked(); } }; #endif // _WIN32 -#endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN struct PageMode: ConfigWizardPage { @@ -572,11 +570,9 @@ struct ConfigWizard::priv PageCustom *page_custom = nullptr; PageUpdate *page_update = nullptr; PageReloadFromDisk *page_reload_from_disk = nullptr; -#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN #ifdef _WIN32 PageFilesAssociation* page_files_association = nullptr; #endif // _WIN32 -#endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN PageMode *page_mode = nullptr; PageVendors *page_vendors = nullptr; Pages3rdparty pages_3rdparty; diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 90e1780903..2fde30cd13 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -835,14 +835,10 @@ bool GUI_App::on_init_inner() if (is_editor()) { #ifdef __WXMSW__ -#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN if (app_config->get("associate_3mf") == "1") -#endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN associate_3mf_files(); -#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN if (app_config->get("associate_stl") == "1") associate_stl_files(); -#endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN #endif // __WXMSW__ preset_updater = new PresetUpdater(); @@ -858,9 +854,7 @@ bool GUI_App::on_init_inner() } else { #ifdef __WXMSW__ -#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN if (app_config->get("associate_gcode") == "1") -#endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN associate_gcode_files(); #endif // __WXMSW__ } @@ -1722,7 +1716,6 @@ void GUI_App::add_config_menu(wxMenuBar *menu) if (dlg.seq_top_layer_only_changed()) #endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER this->plater_->refresh_print(); -#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN #ifdef _WIN32 if (is_editor()) { if (app_config->get("associate_3mf") == "1") @@ -1735,7 +1728,6 @@ void GUI_App::add_config_menu(wxMenuBar *menu) associate_gcode_files(); } #endif // _WIN32 -#endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN } if (app_layout_changed) { // hide full main_sizer for mainFrame @@ -2303,7 +2295,6 @@ void GUI_App::associate_3mf_files() ::SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr); } -#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN void GUI_App::associate_stl_files() { wchar_t app_path[MAX_PATH]; @@ -2327,7 +2318,6 @@ void GUI_App::associate_stl_files() // notify Windows only when any of the values gets changed ::SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr); } -#endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN void GUI_App::associate_gcode_files() { diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 132776dcb3..5572c50712 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -279,13 +279,11 @@ public: bool is_gl_version_greater_or_equal_to(unsigned int major, unsigned int minor) const { return m_opengl_mgr.get_gl_info().is_version_greater_or_equal_to(major, minor); } bool is_glsl_version_greater_or_equal_to(unsigned int major, unsigned int minor) const { return m_opengl_mgr.get_gl_info().is_glsl_version_greater_or_equal_to(major, minor); } -#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN #ifdef __WXMSW__ void associate_3mf_files(); void associate_stl_files(); void associate_gcode_files(); #endif // __WXMSW__ -#endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN private: bool on_init_inner(); @@ -297,13 +295,6 @@ private: bool config_wizard_startup(); void check_updates(const bool verbose); - -#if !ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN -#ifdef __WXMSW__ - void associate_3mf_files(); - void associate_gcode_files(); -#endif // __WXMSW__ -#endif // !ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN }; DECLARE_APP(GUI_App) diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index e28bd589d7..67381cf220 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -106,7 +106,6 @@ void PreferencesDialog::build() option = Option(def, "export_sources_full_pathnames"); m_optgroup_general->append_single_option_line(option); -#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN #ifdef _WIN32 // Please keep in sync with ConfigWizard def.label = L("Associate .3mf files to PrusaSlicer"); @@ -123,7 +122,6 @@ void PreferencesDialog::build() option = Option(def, "associate_stl"); m_optgroup_general->append_single_option_line(option); #endif // _WIN32 -#endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN // Please keep in sync with ConfigWizard def.label = L("Update built-in Presets automatically"); @@ -184,7 +182,6 @@ void PreferencesDialog::build() option = Option(def, "default_action_on_select_preset"); m_optgroup_general->append_single_option_line(option); } -#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN #ifdef _WIN32 else { def.label = L("Associate .gcode files to PrusaSlicer G-code Viewer"); @@ -195,7 +192,6 @@ void PreferencesDialog::build() m_optgroup_general->append_single_option_line(option); } #endif // _WIN32 -#endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN #if __APPLE__ def.label = L("Use Retina resolution for the 3D scene"); From 2d52e155a58740cde9fc9aea5ec860b37c04d5e9 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 26 Feb 2021 13:56:51 +0100 Subject: [PATCH 009/285] Follow-up of 86d7e1fb907d9841a1d0cf516415fea84e8b5280 -> Fixed update after editing custom g-code in settings tabs --- src/slic3r/GUI/Tab.cpp | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 947c058a4e..cf1879bb16 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1732,10 +1732,10 @@ bool Tab::validate_custom_gcode(const wxString& title, const std::string& gcode) return !invalid; } -static void validate_custom_gcode_cb(Tab* tab, ConfigOptionsGroupShp opt_group, const boost::any& value) { +static void validate_custom_gcode_cb(Tab* tab, ConfigOptionsGroupShp opt_group, const t_config_option_key& opt_key, const boost::any& value) { Tab::validate_custom_gcode(opt_group->title, boost::any_cast(value)); tab->update_dirty(); - tab->update(); + tab->on_value_change(opt_key, value); } #endif // ENABLE_VALIDATE_CUSTOM_GCODE @@ -1964,7 +1964,7 @@ void TabFilament::build() optgroup = page->new_optgroup(L("Start G-code"), 0); #if ENABLE_VALIDATE_CUSTOM_GCODE optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(this, optgroup, value); + validate_custom_gcode_cb(this, optgroup, opt_key, value); }; #endif // ENABLE_VALIDATE_CUSTOM_GCODE option = optgroup->get_option("start_filament_gcode"); @@ -1976,7 +1976,7 @@ void TabFilament::build() optgroup = page->new_optgroup(L("End G-code"), 0); #if ENABLE_VALIDATE_CUSTOM_GCODE optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(this, optgroup, value); + validate_custom_gcode_cb(this, optgroup, opt_key, value); }; #endif // ENABLE_VALIDATE_CUSTOM_GCODE option = optgroup->get_option("end_filament_gcode"); @@ -2273,17 +2273,11 @@ void TabPrinter::build_fff() const int gcode_field_height = 15; // 150 const int notes_field_height = 25; // 250 -#if ENABLE_VALIDATE_CUSTOM_GCODE - // WARNING !! - // if you are going to change any of the following optgroup/option titles - // or to add/remove optgroups/options - // update also TabPrinter::validate_custom_gcodes() -#endif // ENABLE_VALIDATE_CUSTOM_GCODE page = add_options_page(L("Custom G-code"), "cog"); optgroup = page->new_optgroup(L("Start G-code"), 0); #if ENABLE_VALIDATE_CUSTOM_GCODE optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(this, optgroup, boost::any_cast(value)); + validate_custom_gcode_cb(this, optgroup, opt_key, value); }; #endif // ENABLE_VALIDATE_CUSTOM_GCODE option = optgroup->get_option("start_gcode"); @@ -2295,7 +2289,7 @@ void TabPrinter::build_fff() optgroup = page->new_optgroup(L("End G-code"), 0); #if ENABLE_VALIDATE_CUSTOM_GCODE optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(this, optgroup, boost::any_cast(value)); + validate_custom_gcode_cb(this, optgroup, opt_key, value); }; #endif // ENABLE_VALIDATE_CUSTOM_GCODE option = optgroup->get_option("end_gcode"); @@ -2307,7 +2301,7 @@ void TabPrinter::build_fff() optgroup = page->new_optgroup(L("Before layer change G-code"), 0); #if ENABLE_VALIDATE_CUSTOM_GCODE optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(this, optgroup, boost::any_cast(value)); + validate_custom_gcode_cb(this, optgroup, opt_key, value); }; #endif // ENABLE_VALIDATE_CUSTOM_GCODE option = optgroup->get_option("before_layer_gcode"); @@ -2319,7 +2313,7 @@ void TabPrinter::build_fff() optgroup = page->new_optgroup(L("After layer change G-code"), 0); #if ENABLE_VALIDATE_CUSTOM_GCODE optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(this, optgroup, boost::any_cast(value)); + validate_custom_gcode_cb(this, optgroup, opt_key, value); }; #endif // ENABLE_VALIDATE_CUSTOM_GCODE option = optgroup->get_option("layer_gcode"); @@ -2331,7 +2325,7 @@ void TabPrinter::build_fff() optgroup = page->new_optgroup(L("Tool change G-code"), 0); #if ENABLE_VALIDATE_CUSTOM_GCODE optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(this, optgroup, boost::any_cast(value)); + validate_custom_gcode_cb(this, optgroup, opt_key, value); }; #endif // ENABLE_VALIDATE_CUSTOM_GCODE option = optgroup->get_option("toolchange_gcode"); @@ -2343,7 +2337,7 @@ void TabPrinter::build_fff() optgroup = page->new_optgroup(L("Between objects G-code (for sequential printing)"), 0); #if ENABLE_VALIDATE_CUSTOM_GCODE optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(this, optgroup, boost::any_cast(value)); + validate_custom_gcode_cb(this, optgroup, opt_key, value); }; #endif // ENABLE_VALIDATE_CUSTOM_GCODE option = optgroup->get_option("between_objects_gcode"); @@ -2355,7 +2349,7 @@ void TabPrinter::build_fff() optgroup = page->new_optgroup(L("Color Change G-code"), 0); #if ENABLE_VALIDATE_CUSTOM_GCODE optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(this, optgroup, boost::any_cast(value)); + validate_custom_gcode_cb(this, optgroup, opt_key, value); }; #endif // ENABLE_VALIDATE_CUSTOM_GCODE option = optgroup->get_option("color_change_gcode"); @@ -2366,7 +2360,7 @@ void TabPrinter::build_fff() optgroup = page->new_optgroup(L("Pause Print G-code"), 0); #if ENABLE_VALIDATE_CUSTOM_GCODE optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(this, optgroup, boost::any_cast(value)); + validate_custom_gcode_cb(this, optgroup, opt_key, value); }; #endif // ENABLE_VALIDATE_CUSTOM_GCODE option = optgroup->get_option("pause_print_gcode"); @@ -2377,7 +2371,7 @@ void TabPrinter::build_fff() optgroup = page->new_optgroup(L("Template Custom G-code"), 0); #if ENABLE_VALIDATE_CUSTOM_GCODE optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) { - validate_custom_gcode_cb(this, optgroup, boost::any_cast(value)); + validate_custom_gcode_cb(this, optgroup, opt_key, value); }; #endif // ENABLE_VALIDATE_CUSTOM_GCODE option = optgroup->get_option("template_custom_gcode"); From c9666ea257e4161bdcf5591e91cca2724f562228 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 26 Feb 2021 14:17:53 +0100 Subject: [PATCH 010/285] #4402 - Review and merge of #6106 Temperature visualization in preview and G-code Viewer by combolek --- src/libslic3r/GCode/GCodeProcessor.cpp | 33 ++++++++++++++++++++++++++ src/libslic3r/GCode/GCodeProcessor.hpp | 10 ++++++++ src/slic3r/GUI/GCodeViewer.cpp | 11 +++++++-- src/slic3r/GUI/GCodeViewer.hpp | 5 ++++ src/slic3r/GUI/GUI_Preview.cpp | 1 + 5 files changed, 58 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index a4769af8e7..c1038bdedb 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -678,6 +678,8 @@ void GCodeProcessor::apply_config(const PrintConfig& config) m_extruder_colors[i] = static_cast(i); } + m_extruder_temps.resize(extruders_count); + m_filament_diameters.resize(config.filament_diameter.values.size()); for (size_t i = 0; i < config.filament_diameter.values.size(); ++i) { m_filament_diameters[i] = static_cast(config.filament_diameter.values[i]); @@ -776,6 +778,8 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) m_extruder_colors[i] = static_cast(i); } + m_extruder_temps.resize(m_result.extruders_count); + const ConfigOptionFloats* filament_load_time = config.option("filament_load_time"); if (filament_load_time != nullptr) { m_time_processor.filament_load_times.resize(filament_load_time->values.size()); @@ -910,6 +914,10 @@ void GCodeProcessor::reset() for (size_t i = 0; i < Min_Extruder_Count; ++i) { m_extruder_colors[i] = static_cast(i); } + m_extruder_temps.resize(Min_Extruder_Count); + for (size_t i = 0; i < Min_Extruder_Count; ++i) { + m_extruder_temps[i] = 0.0f; + } m_filament_diameters = std::vector(Min_Extruder_Count, 1.75f); m_extruded_last_z = 0.0f; @@ -1123,9 +1131,11 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) case 1: { process_M1(line); break; } // Sleep or Conditional stop case 82: { process_M82(line); break; } // Set extruder to absolute mode case 83: { process_M83(line); break; } // Set extruder to relative mode + case 104: { process_M104(line); break; } // Set extruder temperature case 106: { process_M106(line); break; } // Set fan speed case 107: { process_M107(line); break; } // Disable fan case 108: { process_M108(line); break; } // Set tool (Sailfish) + case 109: { process_M109(line); break; } // Set extruder temperature and wait case 132: { process_M132(line); break; } // Recall stored home offsets case 135: { process_M135(line); break; } // Set tool (MakerWare) case 201: { process_M201(line); break; } // Set max printing acceleration @@ -2211,6 +2221,13 @@ void GCodeProcessor::process_M83(const GCodeReader::GCodeLine& line) m_e_local_positioning_type = EPositioningType::Relative; } +void GCodeProcessor::process_M104(const GCodeReader::GCodeLine& line) +{ + float new_temp; + if (line.has_value('S', new_temp)) + m_extruder_temps[m_extruder_id] = new_temp; +} + void GCodeProcessor::process_M106(const GCodeReader::GCodeLine& line) { if (!line.has('P')) { @@ -2243,6 +2260,21 @@ void GCodeProcessor::process_M108(const GCodeReader::GCodeLine& line) process_T(cmd.substr(pos)); } +void GCodeProcessor::process_M109(const GCodeReader::GCodeLine& line) +{ + float new_temp; + if (line.has_value('R', new_temp)) { + float val; + if (line.has_value('T', val)) { + size_t eid = static_cast(val); + if (eid < m_extruder_temps.size()) + m_extruder_temps[eid] = new_temp; + } + else + m_extruder_temps[m_extruder_id] = new_temp; + } +} + void GCodeProcessor::process_M132(const GCodeReader::GCodeLine& line) { // This command is used by Makerbot to load the current home position from EEPROM @@ -2531,6 +2563,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type) m_height, m_mm3_per_mm, m_fan_speed, + m_extruder_temps[m_extruder_id], static_cast(m_result.moves.size()) }; m_result.moves.emplace_back(vertex); diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index f884df8ecc..f619864c44 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -121,6 +121,7 @@ namespace Slic3r { private: using AxisCoords = std::array; using ExtruderColors = std::vector; + using ExtruderTemps = std::vector; enum class EUnits : unsigned char { @@ -211,6 +212,7 @@ namespace Slic3r { float height{ 0.0f }; // mm float mm3_per_mm{ 0.0f }; float fan_speed{ 0.0f }; // percentage + float temperature{ 0.0f }; // Celsius degrees float time{ 0.0f }; // s float volumetric_rate() const { return feedrate * mm3_per_mm; } @@ -320,6 +322,7 @@ namespace Slic3r { float height{ 0.0f }; // mm float mm3_per_mm{ 0.0f }; float fan_speed{ 0.0f }; // percentage + float temperature{ 0.0f }; // Celsius degrees float time{ 0.0f }; // s float volumetric_rate() const { return feedrate * mm3_per_mm; } @@ -468,6 +471,7 @@ namespace Slic3r { ExtrusionRole m_extrusion_role; unsigned char m_extruder_id; ExtruderColors m_extruder_colors; + ExtruderTemps m_extruder_temps; std::vector m_filament_diameters; float m_extruded_last_z; unsigned int m_g1_line_id; @@ -586,6 +590,9 @@ namespace Slic3r { // Set extruder to relative mode void process_M83(const GCodeReader::GCodeLine& line); + // Set extruder temperature + void process_M104(const GCodeReader::GCodeLine& line); + // Set fan speed void process_M106(const GCodeReader::GCodeLine& line); @@ -595,6 +602,9 @@ namespace Slic3r { // Set tool (Sailfish) void process_M108(const GCodeReader::GCodeLine& line); + // Set extruder temperature and wait + void process_M109(const GCodeReader::GCodeLine& line); + // Recall stored home offsets void process_M132(const GCodeReader::GCodeLine& line); diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 71441e037c..e1c61bbd34 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -180,11 +180,13 @@ void GCodeViewer::TBuffer::add_path(const GCodeProcessor::MoveVertex& move, unsi // use rounding to reduce the number of generated paths #if ENABLE_SPLITTED_VERTEX_BUFFER paths.push_back({ move.type, move.extrusion_role, move.delta_extruder, - round_to_nearest(move.height, 2), round_to_nearest(move.width, 2), move.feedrate, move.fan_speed, + round_to_nearest(move.height, 2), round_to_nearest(move.width, 2), + move.feedrate, move.fan_speed, move.temperature, move.volumetric_rate(), move.extruder_id, move.cp_color_id, { { endpoint, endpoint } } }); #else paths.push_back({ move.type, move.extrusion_role, endpoint, endpoint, move.delta_extruder, - round_to_nearest(move.height, 2), round_to_nearest(move.width, 2), move.feedrate, move.fan_speed, + round_to_nearest(move.height, 2), round_to_nearest(move.width, 2), + move.feedrate, move.fan_speed, move.temperature, move.volumetric_rate(), move.extruder_id, move.cp_color_id }); #endif // ENABLE_SPLITTED_VERTEX_BUFFER } @@ -492,6 +494,7 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std: m_extrusions.ranges.height.update_from(round_to_nearest(curr.height, 2)); m_extrusions.ranges.width.update_from(round_to_nearest(curr.width, 2)); m_extrusions.ranges.fan_speed.update_from(curr.fan_speed); + m_extrusions.ranges.temperature.update_from(curr.temperature); m_extrusions.ranges.volumetric_rate.update_from(round_to_nearest(curr.volumetric_rate(), 2)); [[fallthrough]]; } @@ -2725,6 +2728,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool case EViewType::Width: { color = m_extrusions.ranges.width.get_color_at(path.width); break; } case EViewType::Feedrate: { color = m_extrusions.ranges.feedrate.get_color_at(path.feedrate); break; } case EViewType::FanSpeed: { color = m_extrusions.ranges.fan_speed.get_color_at(path.fan_speed); break; } + case EViewType::Temperature: { color = m_extrusions.ranges.temperature.get_color_at(path.temperature); break; } case EViewType::VolumetricRate: { color = m_extrusions.ranges.volumetric_rate.get_color_at(path.volumetric_rate); break; } case EViewType::Tool: { color = m_tool_colors[path.extruder_id]; break; } case EViewType::ColorPrint: { @@ -3184,6 +3188,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool case EViewType::Width: { color = m_extrusions.ranges.width.get_color_at(path.width); break; } case EViewType::Feedrate: { color = m_extrusions.ranges.feedrate.get_color_at(path.feedrate); break; } case EViewType::FanSpeed: { color = m_extrusions.ranges.fan_speed.get_color_at(path.fan_speed); break; } + case EViewType::Temperature: { color = m_extrusions.ranges.temperature.get_color_at(path.temperature); break; } case EViewType::VolumetricRate: { color = m_extrusions.ranges.volumetric_rate.get_color_at(path.volumetric_rate); break; } case EViewType::Tool: { color = m_tool_colors[path.extruder_id]; break; } case EViewType::ColorPrint: { @@ -4014,6 +4019,7 @@ void GCodeViewer::render_legend() const case EViewType::Width: { imgui.title(_u8L("Width (mm)")); break; } case EViewType::Feedrate: { imgui.title(_u8L("Speed (mm/s)")); break; } case EViewType::FanSpeed: { imgui.title(_u8L("Fan Speed (%)")); break; } + case EViewType::Temperature: { imgui.title(_u8L("Temperature (°C)")); break; } case EViewType::VolumetricRate: { imgui.title(_u8L("Volumetric flow rate (mm³/s)")); break; } case EViewType::Tool: { imgui.title(_u8L("Tool")); break; } case EViewType::ColorPrint: { imgui.title(_u8L("Color Print")); break; } @@ -4048,6 +4054,7 @@ void GCodeViewer::render_legend() const case EViewType::Width: { append_range(m_extrusions.ranges.width, 3); break; } case EViewType::Feedrate: { append_range(m_extrusions.ranges.feedrate, 1); break; } case EViewType::FanSpeed: { append_range(m_extrusions.ranges.fan_speed, 0); break; } + case EViewType::Temperature: { append_range(m_extrusions.ranges.temperature, 0); break; } case EViewType::VolumetricRate: { append_range(m_extrusions.ranges.volumetric_rate, 3); break; } case EViewType::Tool: { diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 290c13d51d..2abbb81f13 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -179,6 +179,7 @@ class GCodeViewer float width{ 0.0f }; float feedrate{ 0.0f }; float fan_speed{ 0.0f }; + float temperature{ 0.0f }; float volumetric_rate{ 0.0f }; unsigned char extruder_id{ 0 }; unsigned char cp_color_id{ 0 }; @@ -407,6 +408,8 @@ class GCodeViewer Range fan_speed; // Color mapping by volumetric extrusion rate. Range volumetric_rate; + // Color mapping by extrusion temperature. + Range temperature; void reset() { height.reset(); @@ -414,6 +417,7 @@ class GCodeViewer feedrate.reset(); fan_speed.reset(); volumetric_rate.reset(); + temperature.reset(); } }; @@ -626,6 +630,7 @@ public: Width, Feedrate, FanSpeed, + Temperature, VolumetricRate, Tool, ColorPrint, diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 76677660da..2f11d66f5f 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -211,6 +211,7 @@ bool Preview::init(wxWindow* parent, Model* model) m_choice_view_type->Append(_L("Width")); m_choice_view_type->Append(_L("Speed")); m_choice_view_type->Append(_L("Fan speed")); + m_choice_view_type->Append(_L("Temperature")); m_choice_view_type->Append(_L("Volumetric flow rate")); m_choice_view_type->Append(_L("Tool")); m_choice_view_type->Append(_L("Color Print")); From 9a31580bde1e3cc94cfadfafd3a138154640c3c5 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 26 Feb 2021 16:00:32 +0100 Subject: [PATCH 011/285] GCodeProcessor -> use boost::starts_with() --- src/libslic3r/GCode/GCodeProcessor.cpp | 51 ++++++++++---------------- src/libslic3r/Technologies.hpp | 2 +- 2 files changed, 21 insertions(+), 32 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index c1038bdedb..46fd3c5b3b 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -4,6 +4,9 @@ #include "GCodeProcessor.hpp" #include +#if ENABLE_VALIDATE_CUSTOM_GCODE +#include +#endif // ENABLE_VALIDATE_CUSTOM_GCODE #include #include @@ -594,12 +597,6 @@ const std::vector> GCodeProces unsigned int GCodeProcessor::s_result_id = 0; #if ENABLE_VALIDATE_CUSTOM_GCODE -static inline bool starts_with(const std::string_view comment, const std::string_view tag) -{ - size_t tag_len = tag.size(); - return comment.size() >= tag_len && comment.substr(0, tag_len) == tag; -} - bool GCodeProcessor::contains_reserved_tag(const std::string& gcode, std::string& found_tag) { bool ret = false; @@ -610,7 +607,7 @@ bool GCodeProcessor::contains_reserved_tag(const std::string& gcode, std::string if (comment.length() > 2 && comment.front() == ';') { comment = comment.substr(1); for (const std::string& s : Reserved_Tags) { - if (starts_with(comment, s)) { + if (boost::starts_with(comment, s)) { ret = true; found_tag = comment; parser.quit_parsing(); @@ -635,7 +632,7 @@ bool GCodeProcessor::contains_reserved_tags(const std::string& gcode, unsigned i if (comment.length() > 2 && comment.front() == ';') { comment = comment.substr(1); for (const std::string& s : Reserved_Tags) { - if (starts_with(comment, s)) { + if (boost::starts_with(comment, s)) { ret = true; found_tag.push_back(comment); if (found_tag.size() == max_count) { @@ -1168,14 +1165,6 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) } } -#if !ENABLE_VALIDATE_CUSTOM_GCODE -static inline bool starts_with(const std::string_view comment, const std::string_view tag) -{ - size_t tag_len = tag.size(); - return comment.size() >= tag_len && comment.substr(0, tag_len) == tag; -} -#endif // !ENABLE_VALIDATE_CUSTOM_GCODE - #if __has_include() template struct is_from_chars_convertible : std::false_type {}; @@ -1229,37 +1218,37 @@ void GCodeProcessor::process_tags(const std::string_view comment) #if ENABLE_VALIDATE_CUSTOM_GCODE // extrusion role tag - if (starts_with(comment, reserved_tag(ETags::Role))) { + if (boost::starts_with(comment, reserved_tag(ETags::Role))) { m_extrusion_role = ExtrusionEntity::string_to_role(comment.substr(reserved_tag(ETags::Role).length())); return; } // wipe start tag - if (starts_with(comment, reserved_tag(ETags::Wipe_Start))) { + if (boost::starts_with(comment, reserved_tag(ETags::Wipe_Start))) { m_wiping = true; return; } // wipe end tag - if (starts_with(comment, reserved_tag(ETags::Wipe_End))) { + if (boost::starts_with(comment, reserved_tag(ETags::Wipe_End))) { m_wiping = false; return; } #else // extrusion role tag - if (starts_with(comment, Extrusion_Role_Tag)) { + if (boost::starts_with(comment, Extrusion_Role_Tag)) { m_extrusion_role = ExtrusionEntity::string_to_role(comment.substr(Extrusion_Role_Tag.length())); return; } // wipe start tag - if (starts_with(comment, Wipe_Start_Tag)) { + if (boost::starts_with(comment, Wipe_Start_Tag)) { m_wiping = true; return; } // wipe end tag - if (starts_with(comment, Wipe_End_Tag)) { + if (boost::starts_with(comment, Wipe_End_Tag)) { m_wiping = false; return; } @@ -1268,26 +1257,26 @@ void GCodeProcessor::process_tags(const std::string_view comment) if (!m_producers_enabled || m_producer == EProducer::PrusaSlicer) { #if ENABLE_VALIDATE_CUSTOM_GCODE // height tag - if (starts_with(comment, reserved_tag(ETags::Height))) { + if (boost::starts_with(comment, reserved_tag(ETags::Height))) { if (!parse_number(comment.substr(reserved_tag(ETags::Height).size()), m_forced_height)) BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Height (" << comment << ")."; return; } // width tag - if (starts_with(comment, reserved_tag(ETags::Width))) { + if (boost::starts_with(comment, reserved_tag(ETags::Width))) { if (!parse_number(comment.substr(reserved_tag(ETags::Width).size()), m_forced_width)) BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Width (" << comment << ")."; return; } #else // height tag - if (starts_with(comment, Height_Tag)) { + if (boost::starts_with(comment, Height_Tag)) { if (!parse_number(comment.substr(Height_Tag.size()), m_forced_height)) BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Height (" << comment << ")."; return; } // width tag - if (starts_with(comment, Width_Tag)) { + if (boost::starts_with(comment, Width_Tag)) { if (!parse_number(comment.substr(Width_Tag.size()), m_forced_width)) BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Width (" << comment << ")."; return; @@ -1297,9 +1286,9 @@ void GCodeProcessor::process_tags(const std::string_view comment) #if ENABLE_VALIDATE_CUSTOM_GCODE // color change tag - if (starts_with(comment, reserved_tag(ETags::Color_Change))) { + if (boost::starts_with(comment, reserved_tag(ETags::Color_Change))) { unsigned char extruder_id = 0; - if (starts_with(comment.substr(reserved_tag(ETags::Color_Change).size()), ",T")) { + if (boost::starts_with(comment.substr(reserved_tag(ETags::Color_Change).size()), ",T")) { int eid; if (!parse_number(comment.substr(reserved_tag(ETags::Color_Change).size() + 2), eid) || eid < 0 || eid > 255) { BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Color_Change (" << comment << ")."; @@ -1343,9 +1332,9 @@ void GCodeProcessor::process_tags(const std::string_view comment) } #else // color change tag - if (starts_with(comment, Color_Change_Tag)) { + if (boost::starts_with(comment, Color_Change_Tag)) { unsigned char extruder_id = 0; - if (starts_with(comment.substr(Color_Change_Tag.size()), ",T")) { + if (boost::starts_with(comment.substr(Color_Change_Tag.size()), ",T")) { int eid; if (! parse_number(comment.substr(Color_Change_Tag.size() + 2), eid) || eid < 0 || eid > 255) { BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Color_Change (" << comment << ")."; @@ -1391,7 +1380,7 @@ void GCodeProcessor::process_tags(const std::string_view comment) #if ENABLE_GCODE_VIEWER_DATA_CHECKING // mm3_per_mm print tag - if (starts_with(comment, Mm3_Per_Mm_Tag)) { + if (boost::starts_with(comment, Mm3_Per_Mm_Tag)) { if (! parse_number(comment.substr(Mm3_Per_Mm_Tag.size()), m_mm3_per_mm_compare.last_tag_value)) BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Mm3_Per_Mm (" << comment << ")."; return; diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 77e1712b15..55de9637a6 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -51,7 +51,7 @@ #define ENABLE_WARNING_TEXTURE_REMOVAL (1 && ENABLE_2_3_1_ALPHA1) // Enable showing gcode line numbers in previeww horizontal slider #define ENABLE_GCODE_LINES_ID_IN_H_SLIDER (1 && ENABLE_2_3_1_ALPHA1) -// Enable validation of custom gcode against gcode processor resserved keywords +// Enable validation of custom gcode against gcode processor reserved keywords #define ENABLE_VALIDATE_CUSTOM_GCODE (1 && ENABLE_2_3_1_ALPHA1) From b10d064a24d30a04117a429d096e23851c4df570 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 26 Feb 2021 13:56:09 +0100 Subject: [PATCH 012/285] Added an empty category icon for Fuzzy Skin to prevent crashes in ObjectList The icon should later be replaced by a proper one. --- resources/icons/fuzzy_skin.svg | 8 ++++++++ src/slic3r/GUI/GUI_ObjectList.cpp | 1 + 2 files changed, 9 insertions(+) create mode 100644 resources/icons/fuzzy_skin.svg diff --git a/resources/icons/fuzzy_skin.svg b/resources/icons/fuzzy_skin.svg new file mode 100644 index 0000000000..b8ba0a6513 --- /dev/null +++ b/resources/icons/fuzzy_skin.svg @@ -0,0 +1,8 @@ + + + + + + + diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 5247558c77..ad5ee151d8 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -93,6 +93,7 @@ ObjectList::ObjectList(wxWindow* parent) : CATEGORY_ICON[L("Layers and Perimeters")] = create_scaled_bitmap("layers"); CATEGORY_ICON[L("Infill")] = create_scaled_bitmap("infill"); CATEGORY_ICON[L("Ironing")] = create_scaled_bitmap("ironing"); + CATEGORY_ICON[L("Fuzzy Skin")] = create_scaled_bitmap("fuzzy_skin"); CATEGORY_ICON[L("Support material")] = create_scaled_bitmap("support"); CATEGORY_ICON[L("Speed")] = create_scaled_bitmap("time"); CATEGORY_ICON[L("Extruders")] = create_scaled_bitmap("funnel"); From 6391d4243ff42ddcca10ca22f870cf5bcde77b25 Mon Sep 17 00:00:00 2001 From: Pascal de Bruijn Date: Sat, 27 Feb 2021 17:40:44 +0100 Subject: [PATCH 013/285] creality.ini: tweak fill_density and skirt_distance --- resources/profiles/Creality.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index 9d86d3deab..67ae69a3c3 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -273,7 +273,7 @@ extruder_clearance_height = 34 extruder_clearance_radius = 47 extrusion_width = 0.45 fill_angle = 45 -fill_density = 20% +fill_density = 15% fill_pattern = grid first_layer_extrusion_width = 0.42 first_layer_height = 0.2 @@ -309,7 +309,7 @@ resolution = 0 seam_position = nearest single_extruder_multi_material_priming = 1 skirts = 1 -skirt_distance = 2 +skirt_distance = 3 skirt_height = 2 small_perimeter_speed = 25 solid_infill_below_area = 0 From 19c50c7e694351c493d211c4dcad339702980d28 Mon Sep 17 00:00:00 2001 From: Pascal de Bruijn Date: Sat, 27 Feb 2021 17:51:57 +0100 Subject: [PATCH 014/285] creality.ini: consolidate more properties to *common* --- resources/profiles/Creality.ini | 107 +++++++++++++------------------- 1 file changed, 42 insertions(+), 65 deletions(-) diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index 67ae69a3c3..2bc03751eb 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -712,7 +712,7 @@ printer_technology = FFF before_layer_gcode = ;BEFORE_LAYER_CHANGE\nG92 E0\n;[layer_z]\n\n between_objects_gcode = pause_print_gcode = -deretract_speed = 0 +deretract_speed = 40 extruder_colour = #FCE94F extruder_offset = 0x0 gcode_flavor = marlin @@ -735,23 +735,24 @@ machine_max_jerk_z = 0.4 machine_min_extruding_rate = 0 machine_min_travel_rate = 0 layer_gcode = ;AFTER_LAYER_CHANGE\n;[layer_z] -max_layer_height = 0.3 -min_layer_height = 0.07 +max_layer_height = 0.28 +min_layer_height = 0.08 max_print_height = 250 nozzle_diameter = 0.4 printer_notes = printer_settings_id = -retract_before_travel = 1 -retract_before_wipe = 0% +printer_variant = 0.4 +retract_before_travel = 2 +retract_before_wipe = 70% retract_layer_change = 1 -retract_length = 1 +retract_length = 5 retract_length_toolchange = 1 retract_lift = 0 retract_lift_above = 0 retract_lift_below = 0 retract_restart_extra = 0 retract_restart_extra_toolchange = 0 -retract_speed = 35 +retract_speed = 60 single_extruder_multi_material = 0 toolchange_gcode = use_firmware_retraction = 0 @@ -763,39 +764,6 @@ z_offset = 0 printer_model = default_print_profile = 0.16mm OPTIMAL @CREALITY default_filament_profile = Generic PLA @CREALITY - -[printer:Creality Ender-3] -inherits = *common* -renamed_from = "Creality ENDER-3" -printer_model = ENDER3 -printer_variant = 0.4 -max_layer_height = 0.28 -min_layer_height = 0.08 -printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER3\nPRINTER_HAS_BOWDEN -bed_shape = 3x3,228x3,228x228,3x228 -max_print_height = 250 -machine_max_acceleration_e = 5000 -machine_max_acceleration_extruding = 500 -machine_max_acceleration_retracting = 1000 -machine_max_acceleration_x = 500 -machine_max_acceleration_y = 500 -machine_max_acceleration_z = 100 -machine_max_feedrate_e = 60 -machine_max_feedrate_x = 500 -machine_max_feedrate_y = 500 -machine_max_feedrate_z = 10 -machine_max_jerk_e = 5 -machine_max_jerk_x = 8 -machine_max_jerk_y = 8 -machine_max_jerk_z = 0.4 -machine_min_extruding_rate = 0 -machine_min_travel_rate = 0 -nozzle_diameter = 0.4 -retract_before_travel = 2 -retract_length = 5 -retract_speed = 60 -deretract_speed = 40 -retract_before_wipe = 70% start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S120 ; set temporary nozzle temp to prevent oozing during homing and auto bed leveling\nM140 S[first_layer_bed_temperature] ; set final bed temp\nG4 S10 ; allow partial nozzle warmup\nG28 ; home all axis\nG1 Z50 F240\nG1 X2 Y10 F3000\nM104 S[first_layer_temperature] ; set final nozzle temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp to stabilize\nM109 S[first_layer_temperature] ; wait for nozzle temp to stabilize\nG1 Z0.28 F240\nG92 E0\nG1 Y140 E10 F1500 ; prime the nozzle\nG1 X2.3 F5000\nG92 E0\nG1 Y10 E10 F1200 ; prime the nozzle\nG92 E0 end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+2, max_print_height)} F600 ; Move print head up{endif}\nG1 X5 Y{print_bed_max[1]*0.8} F{travel_speed*60} ; present print\n{if max_layer_z < max_print_height-10}G1 Z{z_offset+min(max_layer_z+70, max_print_height-10)} F600 ; Move print head further up{endif}\n{if max_layer_z < max_print_height*0.6}G1 Z{max_print_height*0.6} F600 ; Move print head further up{endif}\nM140 S0 ; turn off heatbed\nM104 S0 ; turn off temperature\nM107 ; turn off fan\nM84 X Y E ; disable motors @@ -842,19 +810,28 @@ retract_restart_extra_toolchange = 0,0 retract_speed = 60,60 wipe = 1,1 +[printer:Creality Ender-3] +inherits = *common* +renamed_from = "Creality ENDER-3" +bed_shape = 3x3,228x3,228x228,3x228 +max_print_height = 250 +printer_model = ENDER3 +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER3\nPRINTER_HAS_BOWDEN + [printer:Creality Ender-3 BLTouch] inherits = Creality Ender-3; *fastabl* renamed_from = "Creality ENDER-3 BLTouch" printer_model = ENDER3BLTOUCH [printer:Creality Ender-3 V2] -inherits = Creality Ender-3 +inherits = *common* renamed_from = "Creality Ender-3V2" -printer_model = ENDER3V2 bed_shape = 5x0,215x0,215x220,5x220 +max_print_height = 250 +printer_model = ENDER3V2 #[printer:Creality Ender-3 Max] -#inherits = Creality Ender-3 +#inherits = *common* #retract_length = 6 #bed_shape = 5x5,295x5,295x295,5x295 #max_print_height = 340 @@ -862,14 +839,14 @@ bed_shape = 5x0,215x0,215x220,5x220 #printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER3MAX\nPRINTER_HAS_BOWDEN #[printer:Creality Ender-4] -#inherits = Creality Ender-3; *descendingz* +#inherits = *common*; *descendingz* #bed_shape = 5x0,215x0,215x220,5x220 #max_print_height = 300 #printer_model = ENDER4 #printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER4\nPRINTER_HAS_BOWDEN [printer:Creality Ender-5] -inherits = Creality Ender-3; *descendingz* +inherits = *common*; *descendingz* retract_length = 6 bed_shape = 5x2.5,225x2.5,225x222.5,5x222.5 max_print_height = 300 @@ -879,7 +856,7 @@ machine_max_acceleration_e = 1000 machine_max_feedrate_z = 5 [printer:Creality Ender-5 Plus] -inherits = Creality Ender-3; *slowabl*; *descendingz* +inherits = *common*; *slowabl*; *descendingz* retract_length = 6 bed_shape = 5x5,355x5,355x355,5x355 max_print_height = 400 @@ -891,14 +868,14 @@ machine_max_feedrate_x = 300 machine_max_feedrate_y = 300 #[printer:Creality Ender-6] -#inherits = Creality Ender-3; *descendingz* +#inherits = *common*; *descendingz* #bed_shape = 5x5,255x5,255x255,5x255 #max_print_height = 400 #printer_model = ENDER6 #printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER6\nPRINTER_HAS_BOWDEN [printer:Creality Ender-2] -inherits = Creality Ender-3 +inherits = *common* renamed_from = "Creality ENDER-2" bed_shape = 0x0,150x0,150x150,0x150 max_print_height = 200 @@ -906,13 +883,13 @@ printer_model = ENDER2 printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER2\nPRINTER_HAS_BOWDEN #[printer:Creality CR-6 SE] -#inherits = Creality Ender-3; *fastabl*; *pauseprint* +#inherits = *common*; *fastabl*; *pauseprint* #bed_shape = 5x0,230x0,230x235,5x235 #printer_model = CR6SE #printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR6SE\nPRINTER_HAS_BOWDEN #[printer:Creality CR-6 Max] -#inherits = Creality Ender-3; *slowabl* +#inherits = *common*; *slowabl* #retract_length = 6 #bed_shape = 5x5,395x5,395x395,5x395 #max_print_height = 400 @@ -920,7 +897,7 @@ printer_notes = Don't remove the following keywords! These keywords are used in #printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR6MAX\nPRINTER_HAS_BOWDEN [printer:Creality CR-10 Mini] -inherits = Creality Ender-3 +inherits = *common* retract_length = 6 bed_shape = 2.5x5,2.5x225,302.5x225,302.5x5 max_print_height = 300 @@ -928,7 +905,7 @@ printer_model = CR10MINI printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR10MINI\nPRINTER_HAS_BOWDEN #[printer:Creality CR-10 Max] -#inherits = Creality Ender-3; *slowabl* +#inherits = *common*; *slowabl* #retract_length = 6 #bed_shape = 5x5,445x5,445x445,5x445 #max_print_height = 470 @@ -936,7 +913,7 @@ printer_notes = Don't remove the following keywords! These keywords are used in #printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR10MAX\nPRINTER_HAS_BOWDEN [printer:Creality CR-10] -inherits = Creality Ender-3 +inherits = *common* retract_length = 6 bed_shape = 5x5,305x5,305x305,5x305 max_print_height = 400 @@ -944,7 +921,7 @@ printer_model = CR10 printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR10\nPRINTER_HAS_BOWDEN [printer:Creality CR-10 V2] -inherits = Creality Ender-3 +inherits = *common* retract_length = 6 bed_shape = 5x5,305x5,305x305,5x305 max_print_height = 400 @@ -952,7 +929,7 @@ printer_model = CR10V2 printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR10V2\nPRINTER_HAS_BOWDEN [printer:Creality CR-10 V3] -inherits = Creality Ender-3 +inherits = *common* retract_length = 1 bed_shape = 5x5,305x5,305x305,5x305 max_print_height = 400 @@ -960,7 +937,7 @@ printer_model = CR10V3 printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR10V3 [printer:Creality CR-10 S] -inherits = Creality Ender-3 +inherits = *common* retract_length = 6 bed_shape = 5x5,305x5,305x305,5x305 max_print_height = 400 @@ -968,7 +945,7 @@ printer_model = CR10S printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR10S\nPRINTER_HAS_BOWDEN [printer:Creality CR-10 S Pro] -inherits = Creality Ender-3; *slowabl* +inherits = *common*; *slowabl* retract_length = 6 bed_shape = 5x5,295x5,295x295,5x295 max_print_height = 400 @@ -976,7 +953,7 @@ printer_model = CR10SPRO printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR10SPRO\nPRINTER_HAS_BOWDEN [printer:Creality CR-10 S Pro V2] -inherits = Creality Ender-3; *slowabl* +inherits = *common*; *slowabl* retract_length = 6 bed_shape = 5x5,305x5,305x305,5x305 max_print_height = 400 @@ -984,7 +961,7 @@ printer_model = CR10SPROV2 printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR10SPROV2\nPRINTER_HAS_BOWDEN [printer:Creality CR-10 S4] -inherits = Creality Ender-3 +inherits = *common* retract_length = 6 bed_shape = 5x5,395x5,395x395,5x395 max_print_height = 400 @@ -992,7 +969,7 @@ printer_model = CR10S4 printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR10S4\nPRINTER_HAS_BOWDEN [printer:Creality CR-10 S5] -inherits = Creality Ender-3 +inherits = *common* retract_length = 6 bed_shape = 5x5,505x5,505x505,5x505 max_print_height = 500 @@ -1000,25 +977,25 @@ printer_model = CR10S5 printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR10S5\nPRINTER_HAS_BOWDEN [printer:Creality CR-20] -inherits = Creality Ender-3 +inherits = *common* printer_model = CR20 printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR20\nPRINTER_HAS_BOWDEN [printer:Creality CR-20 Pro] -inherits = Creality Ender-3; *fastabl* +inherits = *common*; *fastabl* retract_length = 4 printer_model = CR20PRO printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR20PRO\nPRINTER_HAS_BOWDEN #[printer:Creality CR-8] -#inherits = Creality Ender-3 +#inherits = *common* #bed_shape = 5x5,215x5,215x215,5x215 #max_print_height = 210 #printer_model = CR8 #printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR8\nPRINTER_HAS_BOWDEN #[printer:Creality CR-X] -#inherits = Creality Ender-3; *dualextruder* +#inherits = *common*; *dualextruder* #retract_length = 6,6 #bed_shape = 5x5,295x5,295x295,5x295 #max_print_height = 400 @@ -1026,7 +1003,7 @@ printer_notes = Don't remove the following keywords! These keywords are used in #printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CRX\nPRINTER_HAS_BOWDEN #[printer:Creality CR-X Pro] -#inherits = Creality Ender-3; *dualextruder*; *slowabl* +#inherits = *common*; *dualextruder*; *slowabl* #retract_length = 6,6 #bed_shape = 5x5,295x5,295x295,5x295 #max_print_height = 400 From c142af74c52d28eb3aa9c80a07760f421c78a38f Mon Sep 17 00:00:00 2001 From: Pascal de Bruijn Date: Sat, 27 Feb 2021 17:53:20 +0100 Subject: [PATCH 015/285] creality.ini: add printer_notes to Ender3V2 --- resources/profiles/Creality.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index 2bc03751eb..c439ed0e42 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -829,6 +829,7 @@ renamed_from = "Creality Ender-3V2" bed_shape = 5x0,215x0,215x220,5x220 max_print_height = 250 printer_model = ENDER3V2 +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER3V2\nPRINTER_HAS_BOWDEN #[printer:Creality Ender-3 Max] #inherits = *common* From bc9c968f0546fa0531931e3f9cdc34a330c15305 Mon Sep 17 00:00:00 2001 From: Pascal de Bruijn Date: Sat, 27 Feb 2021 18:16:47 +0100 Subject: [PATCH 016/285] creality.ini disable single_extruder_multi_material_priming --- resources/profiles/Creality.ini | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index c439ed0e42..6dfe32b40e 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -307,7 +307,7 @@ print_settings_id = raft_layers = 0 resolution = 0 seam_position = nearest -single_extruder_multi_material_priming = 1 +single_extruder_multi_material_priming = 0 skirts = 1 skirt_distance = 3 skirt_height = 2 @@ -787,6 +787,7 @@ end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+2, # Intended for printers with dual extruders and a single hotend/nozzle, like the CR-X series [printer:*dualextruder*] single_extruder_multi_material = 1 +single_extruder_multi_material_priming = 0 cooling_tube_length = 5 cooling_tube_retraction = 91.5 extra_loading_move = -2 From 249670f82c04d5d9d475ea6b38dc191beac32295 Mon Sep 17 00:00:00 2001 From: Pascal de Bruijn Date: Sun, 28 Feb 2021 16:36:58 +0100 Subject: [PATCH 017/285] creality.ini: fix CR-10 Mini bed_shape --- resources/profiles/Creality.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index 6dfe32b40e..67b1082090 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -901,7 +901,7 @@ printer_notes = Don't remove the following keywords! These keywords are used in [printer:Creality CR-10 Mini] inherits = *common* retract_length = 6 -bed_shape = 2.5x5,2.5x225,302.5x225,302.5x5 +bed_shape = 2.5x5,302.5x5,302.5x225,2.5x225 max_print_height = 300 printer_model = CR10MINI printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR10MINI\nPRINTER_HAS_BOWDEN From 2d762ec3202e9d7e400db4b20e3e5630098f6a19 Mon Sep 17 00:00:00 2001 From: Pascal de Bruijn Date: Sun, 28 Feb 2021 16:38:05 +0100 Subject: [PATCH 018/285] creality.ini: add CR-5 Pro retract_length = 3 to prevent clogging since this printer is suspected to have an all metal hot end, as Creality claims print temperatures up to 300C --- resources/profiles/Creality.ini | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index 67b1082090..790c352ff9 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -95,6 +95,15 @@ bed_model = ender2_bed.stl bed_texture = ender2.svg default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY +#[printer_model:CR5PRO] +#name = Creality CR-5 Pro +#variants = 0.4 +#technology = FFF +#family = CR +#bed_model = cr10mini_bed.stl +#bed_texture = cr10mini.svg +#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY + #[printer_model:CR6SE] #name = Creality CR-6 SE #variants = 0.4 @@ -884,6 +893,14 @@ max_print_height = 200 printer_model = ENDER2 printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER2\nPRINTER_HAS_BOWDEN +#[printer:Creality CR-5 Pro] +#inherits = *common*; *slowabl*; *descendingz* +#retract_length = 3 +#bed_shape = 2.5x5,302.5x5,302.5x225,2.5x225 +#max_print_height = 380 +#printer_model = CR5PRO +#printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR5PRO\nPRINTER_HAS_BOWDEN + #[printer:Creality CR-6 SE] #inherits = *common*; *fastabl*; *pauseprint* #bed_shape = 5x0,230x0,230x235,5x235 From 19ba6f14927d47fad09410d56f927d861f55560d Mon Sep 17 00:00:00 2001 From: Pascal de Bruijn Date: Sun, 28 Feb 2021 16:53:33 +0100 Subject: [PATCH 019/285] creality.ini: add CR-200B --- resources/profiles/Creality.ini | 14 + resources/profiles/Creality/cr200b.svg | 4 + resources/profiles/Creality/cr200b_bed.stl | 2774 ++++++++++++++++++++ 3 files changed, 2792 insertions(+) create mode 100644 resources/profiles/Creality/cr200b.svg create mode 100644 resources/profiles/Creality/cr200b_bed.stl diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index 790c352ff9..2e9971f21f 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -230,6 +230,15 @@ bed_model = ender3_bed.stl bed_texture = cr20.svg default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY +#[printer_model:CR200B] +#name = Creality CR-200B +#variants = 0.4 +#technology = FFF +#family = CR +#bed_model = cr200b_bed.stl +#bed_texture = cr200b.svg +#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY + #[printer_model:CR8] #name = Creality CR-8 #variants = 0.4 @@ -1006,6 +1015,11 @@ retract_length = 4 printer_model = CR20PRO printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR20PRO\nPRINTER_HAS_BOWDEN +#[printer:Creality CR-200B] +#inherits = *common* +#printer_model = CR200B +#printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR200B\nPRINTER_HAS_BOWDEN + #[printer:Creality CR-8] #inherits = *common* #bed_shape = 5x5,215x5,215x215,5x215 diff --git a/resources/profiles/Creality/cr200b.svg b/resources/profiles/Creality/cr200b.svg new file mode 100644 index 0000000000..6012188d7f --- /dev/null +++ b/resources/profiles/Creality/cr200b.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/profiles/Creality/cr200b_bed.stl b/resources/profiles/Creality/cr200b_bed.stl new file mode 100644 index 0000000000..210c329e71 --- /dev/null +++ b/resources/profiles/Creality/cr200b_bed.stl @@ -0,0 +1,2774 @@ +solid OpenSCAD_Model + facet normal 0 0 -1 + outer loop + vertex 97.1047 -99.9982 -3 + vertex 97.0018 -99.9982 -3 + vertex 97.3136 -99.9836 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 97.3136 -99.9836 -3 + vertex 97.0018 -99.9982 -3 + vertex 97.5209 -99.9544 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 100 -97 -3 + vertex 97.0018 -99.9982 -3 + vertex 97.0018 99.9982 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 97.5209 -99.9544 -3 + vertex 97.0018 -99.9982 -3 + vertex 97.7258 -99.9109 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 97.7258 -99.9109 -3 + vertex 97.0018 -99.9982 -3 + vertex 97.927 -99.8532 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 97.927 -99.8532 -3 + vertex 97.0018 -99.9982 -3 + vertex 98.1238 -99.7815 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 98.1238 -99.7815 -3 + vertex 97.0018 -99.9982 -3 + vertex 98.3151 -99.6964 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 98.3151 -99.6964 -3 + vertex 97.0018 -99.9982 -3 + vertex 98.5 -99.5981 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 98.5 -99.5981 -3 + vertex 97.0018 -99.9982 -3 + vertex 98.6776 -99.4871 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 98.6776 -99.4871 -3 + vertex 97.0018 -99.9982 -3 + vertex 98.847 -99.364 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 98.847 -99.364 -3 + vertex 97.0018 -99.9982 -3 + vertex 99.0074 -99.2294 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 99.0074 -99.2294 -3 + vertex 97.0018 -99.9982 -3 + vertex 99.158 -99.084 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 99.158 -99.084 -3 + vertex 97.0018 -99.9982 -3 + vertex 99.2981 -98.9284 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 99.2981 -98.9284 -3 + vertex 97.0018 -99.9982 -3 + vertex 99.427 -98.7634 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 99.427 -98.7634 -3 + vertex 97.0018 -99.9982 -3 + vertex 99.5441 -98.5898 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 99.5441 -98.5898 -3 + vertex 97.0018 -99.9982 -3 + vertex 99.6488 -98.4084 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 99.6488 -98.4084 -3 + vertex 97.0018 -99.9982 -3 + vertex 99.7406 -98.2202 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 99.7406 -98.2202 -3 + vertex 97.0018 -99.9982 -3 + vertex 99.8191 -98.026 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 99.8191 -98.026 -3 + vertex 97.0018 -99.9982 -3 + vertex 99.8838 -97.8269 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 99.8838 -97.8269 -3 + vertex 97.0018 -99.9982 -3 + vertex 99.9344 -97.6237 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 99.9344 -97.6237 -3 + vertex 97.0018 -99.9982 -3 + vertex 99.9708 -97.4175 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 99.9708 -97.4175 -3 + vertex 97.0018 -99.9982 -3 + vertex 99.9927 -97.2093 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 99.9927 -97.2093 -3 + vertex 97.0018 -99.9982 -3 + vertex 100 -97 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 100 -97 -3 + vertex 97.0018 99.9982 -3 + vertex 100 97 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 97.0018 99.9982 -3 + vertex 99.9708 97.4175 -3 + vertex 99.9927 97.2093 -3 + endloop + endfacet + facet normal -0 -0 -1 + outer loop + vertex 99.8838 97.8269 -3 + vertex 99.9344 97.6237 -3 + vertex 97.0018 99.9982 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 99.7406 98.2202 -3 + vertex 97.0018 99.9982 -3 + vertex 99.6488 98.4084 -3 + endloop + endfacet + facet normal -0 -0 -1 + outer loop + vertex 99.7406 98.2202 -3 + vertex 99.8191 98.026 -3 + vertex 97.0018 99.9982 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 97.0018 99.9982 -3 + vertex 99.0074 99.2294 -3 + vertex 99.158 99.084 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 99.427 98.7634 -3 + vertex 97.0018 99.9982 -3 + vertex 99.2981 98.9284 -3 + endloop + endfacet + facet normal -0 -0 -1 + outer loop + vertex 99.427 98.7634 -3 + vertex 99.5441 98.5898 -3 + vertex 97.0018 99.9982 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 99.2981 98.9284 -3 + vertex 97.0018 99.9982 -3 + vertex 99.158 99.084 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 97.0018 99.9982 -3 + vertex 97.927 99.8532 -3 + vertex 98.1238 99.7815 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 98.847 99.364 -3 + vertex 97.0018 99.9982 -3 + vertex 98.6776 99.4871 -3 + endloop + endfacet + facet normal -0 -0 -1 + outer loop + vertex 98.847 99.364 -3 + vertex 99.0074 99.2294 -3 + vertex 97.0018 99.9982 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 98.6776 99.4871 -3 + vertex 97.0018 99.9982 -3 + vertex 98.5 99.5981 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 98.5 99.5981 -3 + vertex 97.0018 99.9982 -3 + vertex 98.3151 99.6964 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 98.3151 99.6964 -3 + vertex 97.0018 99.9982 -3 + vertex 98.1238 99.7815 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 99.6488 98.4084 -3 + vertex 97.0018 99.9982 -3 + vertex 99.5441 98.5898 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 97.7258 99.9109 -3 + vertex 97.0018 99.9982 -3 + vertex 97.5209 99.9544 -3 + endloop + endfacet + facet normal -0 -0 -1 + outer loop + vertex 97.7258 99.9109 -3 + vertex 97.927 99.8532 -3 + vertex 97.0018 99.9982 -3 + endloop + endfacet + facet normal -0 -0 -1 + outer loop + vertex 99.8191 98.026 -3 + vertex 99.8838 97.8269 -3 + vertex 97.0018 99.9982 -3 + endloop + endfacet + facet normal -0 -0 -1 + outer loop + vertex 97.3136 99.9836 -3 + vertex 97.5209 99.9544 -3 + vertex 97.0018 99.9982 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 97.0018 99.9982 -3 + vertex 99.9344 97.6237 -3 + vertex 99.9708 97.4175 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 97.0018 99.9982 -3 + vertex 97.1047 99.9982 -3 + vertex 97.3136 99.9836 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 100 97 -3 + vertex 97.0018 99.9982 -3 + vertex 99.9927 97.2093 -3 + endloop + endfacet + facet normal 0 -0 -1 + outer loop + vertex -97 100 -3 + vertex 97 100 -3 + vertex -97.0018 99.9982 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.0018 99.9982 -3 + vertex 97 100 -3 + vertex 97.0018 99.9982 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 97.0018 -99.9982 -3 + vertex -97.0018 99.9982 -3 + vertex 97.0018 99.9982 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -100 -97 -3 + vertex -100 97 -3 + vertex -97.0018 -99.9982 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex -97.0018 99.9982 -3 + vertex -97.3136 99.9836 -3 + vertex -97.1047 99.9982 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.0018 99.9982 -3 + vertex -97.5209 99.9544 -3 + vertex -97.3136 99.9836 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.0018 99.9982 -3 + vertex -97.7258 99.9109 -3 + vertex -97.5209 99.9544 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.0018 99.9982 -3 + vertex -97.927 99.8532 -3 + vertex -97.7258 99.9109 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.0018 99.9982 -3 + vertex -98.1238 99.7815 -3 + vertex -97.927 99.8532 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.0018 99.9982 -3 + vertex -98.3151 99.6964 -3 + vertex -98.1238 99.7815 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.0018 99.9982 -3 + vertex -98.5 99.5981 -3 + vertex -98.3151 99.6964 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.0018 99.9982 -3 + vertex -98.6776 99.4871 -3 + vertex -98.5 99.5981 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.0018 99.9982 -3 + vertex -98.847 99.364 -3 + vertex -98.6776 99.4871 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.0018 99.9982 -3 + vertex -99.0074 99.2294 -3 + vertex -98.847 99.364 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.0018 99.9982 -3 + vertex -99.158 99.084 -3 + vertex -99.0074 99.2294 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.0018 99.9982 -3 + vertex -99.2981 98.9284 -3 + vertex -99.158 99.084 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.0018 99.9982 -3 + vertex -99.427 98.7634 -3 + vertex -99.2981 98.9284 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.0018 99.9982 -3 + vertex -99.5441 98.5898 -3 + vertex -99.427 98.7634 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.0018 99.9982 -3 + vertex -99.6488 98.4084 -3 + vertex -99.5441 98.5898 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.0018 99.9982 -3 + vertex -99.7406 98.2202 -3 + vertex -99.6488 98.4084 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.0018 99.9982 -3 + vertex -99.8191 98.026 -3 + vertex -99.7406 98.2202 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.0018 99.9982 -3 + vertex -99.8838 97.8269 -3 + vertex -99.8191 98.026 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.0018 99.9982 -3 + vertex -99.9344 97.6237 -3 + vertex -99.8838 97.8269 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.0018 99.9982 -3 + vertex -99.9708 97.4175 -3 + vertex -99.9344 97.6237 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.0018 99.9982 -3 + vertex -99.9927 97.2093 -3 + vertex -99.9708 97.4175 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.0018 99.9982 -3 + vertex -100 97 -3 + vertex -99.9927 97.2093 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.0018 -99.9982 -3 + vertex -100 97 -3 + vertex -97.0018 99.9982 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 97.0018 -99.9982 -3 + vertex -97.0018 -99.9982 -3 + vertex -97.0018 99.9982 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.0018 -99.9982 -3 + vertex -99.9708 -97.4175 -3 + vertex -99.9927 -97.2093 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -99.8838 -97.8269 -3 + vertex -99.9344 -97.6237 -3 + vertex -97.0018 -99.9982 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -99.7406 -98.2202 -3 + vertex -97.0018 -99.9982 -3 + vertex -99.6488 -98.4084 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -99.7406 -98.2202 -3 + vertex -99.8191 -98.026 -3 + vertex -97.0018 -99.9982 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.0018 -99.9982 -3 + vertex -99.0074 -99.2294 -3 + vertex -99.158 -99.084 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -99.427 -98.7634 -3 + vertex -97.0018 -99.9982 -3 + vertex -99.2981 -98.9284 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -99.427 -98.7634 -3 + vertex -99.5441 -98.5898 -3 + vertex -97.0018 -99.9982 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -99.2981 -98.9284 -3 + vertex -97.0018 -99.9982 -3 + vertex -99.158 -99.084 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.0018 -99.9982 -3 + vertex -97.927 -99.8532 -3 + vertex -98.1238 -99.7815 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -98.847 -99.364 -3 + vertex -97.0018 -99.9982 -3 + vertex -98.6776 -99.4871 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -98.847 -99.364 -3 + vertex -99.0074 -99.2294 -3 + vertex -97.0018 -99.9982 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -98.6776 -99.4871 -3 + vertex -97.0018 -99.9982 -3 + vertex -98.5 -99.5981 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -98.5 -99.5981 -3 + vertex -97.0018 -99.9982 -3 + vertex -98.3151 -99.6964 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -98.3151 -99.6964 -3 + vertex -97.0018 -99.9982 -3 + vertex -98.1238 -99.7815 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -99.6488 -98.4084 -3 + vertex -97.0018 -99.9982 -3 + vertex -99.5441 -98.5898 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.7258 -99.9109 -3 + vertex -97.0018 -99.9982 -3 + vertex -97.5209 -99.9544 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.7258 -99.9109 -3 + vertex -97.927 -99.8532 -3 + vertex -97.0018 -99.9982 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -99.8191 -98.026 -3 + vertex -99.8838 -97.8269 -3 + vertex -97.0018 -99.9982 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.3136 -99.9836 -3 + vertex -97.5209 -99.9544 -3 + vertex -97.0018 -99.9982 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.0018 -99.9982 -3 + vertex -99.9344 -97.6237 -3 + vertex -99.9708 -97.4175 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.0018 -99.9982 -3 + vertex -97.1047 -99.9982 -3 + vertex -97.3136 -99.9836 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 97.0018 -99.9982 -3 + vertex 97 -100 -3 + vertex -97.0018 -99.9982 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -97.0018 -99.9982 -3 + vertex 97 -100 -3 + vertex -97 -100 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -100 -97 -3 + vertex -97.0018 -99.9982 -3 + vertex -99.9927 -97.2093 -3 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 97.3136 -99.9836 0 + vertex 97.0018 -99.9982 0 + vertex 97.1047 -99.9982 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 97.5209 -99.9544 0 + vertex 97.0018 -99.9982 0 + vertex 97.3136 -99.9836 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 97.0018 99.9982 0 + vertex 97.0018 -99.9982 0 + vertex 100 -97 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 97.7258 -99.9109 0 + vertex 97.0018 -99.9982 0 + vertex 97.5209 -99.9544 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 97.927 -99.8532 0 + vertex 97.0018 -99.9982 0 + vertex 97.7258 -99.9109 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 98.1238 -99.7815 0 + vertex 97.0018 -99.9982 0 + vertex 97.927 -99.8532 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 98.3151 -99.6964 0 + vertex 97.0018 -99.9982 0 + vertex 98.1238 -99.7815 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 98.5 -99.5981 0 + vertex 97.0018 -99.9982 0 + vertex 98.3151 -99.6964 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 98.6776 -99.4871 0 + vertex 97.0018 -99.9982 0 + vertex 98.5 -99.5981 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 98.847 -99.364 0 + vertex 97.0018 -99.9982 0 + vertex 98.6776 -99.4871 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 99.0074 -99.2294 0 + vertex 97.0018 -99.9982 0 + vertex 98.847 -99.364 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 99.158 -99.084 0 + vertex 97.0018 -99.9982 0 + vertex 99.0074 -99.2294 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 99.2981 -98.9284 0 + vertex 97.0018 -99.9982 0 + vertex 99.158 -99.084 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 99.427 -98.7634 0 + vertex 97.0018 -99.9982 0 + vertex 99.2981 -98.9284 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 99.5441 -98.5898 0 + vertex 97.0018 -99.9982 0 + vertex 99.427 -98.7634 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 99.6488 -98.4084 0 + vertex 97.0018 -99.9982 0 + vertex 99.5441 -98.5898 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 99.7406 -98.2202 0 + vertex 97.0018 -99.9982 0 + vertex 99.6488 -98.4084 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 99.8191 -98.026 0 + vertex 97.0018 -99.9982 0 + vertex 99.7406 -98.2202 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 99.8838 -97.8269 0 + vertex 97.0018 -99.9982 0 + vertex 99.8191 -98.026 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 99.9344 -97.6237 0 + vertex 97.0018 -99.9982 0 + vertex 99.8838 -97.8269 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 99.9708 -97.4175 0 + vertex 97.0018 -99.9982 0 + vertex 99.9344 -97.6237 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 99.9927 -97.2093 0 + vertex 97.0018 -99.9982 0 + vertex 99.9708 -97.4175 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 100 -97 0 + vertex 97.0018 -99.9982 0 + vertex 99.9927 -97.2093 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 100 97 0 + vertex 97.0018 99.9982 0 + vertex 100 -97 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 99.9927 97.2093 0 + vertex 99.9708 97.4175 0 + vertex 97.0018 99.9982 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 97.0018 99.9982 0 + vertex 99.9344 97.6237 0 + vertex 99.8838 97.8269 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 99.6488 98.4084 0 + vertex 97.0018 99.9982 0 + vertex 99.7406 98.2202 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 97.0018 99.9982 0 + vertex 99.8191 98.026 0 + vertex 99.7406 98.2202 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 99.158 99.084 0 + vertex 99.0074 99.2294 0 + vertex 97.0018 99.9982 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 99.2981 98.9284 0 + vertex 97.0018 99.9982 0 + vertex 99.427 98.7634 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 97.0018 99.9982 0 + vertex 99.5441 98.5898 0 + vertex 99.427 98.7634 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 99.158 99.084 0 + vertex 97.0018 99.9982 0 + vertex 99.2981 98.9284 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 98.1238 99.7815 0 + vertex 97.927 99.8532 0 + vertex 97.0018 99.9982 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 98.6776 99.4871 0 + vertex 97.0018 99.9982 0 + vertex 98.847 99.364 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 97.0018 99.9982 0 + vertex 99.0074 99.2294 0 + vertex 98.847 99.364 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 98.5 99.5981 0 + vertex 97.0018 99.9982 0 + vertex 98.6776 99.4871 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 98.3151 99.6964 0 + vertex 97.0018 99.9982 0 + vertex 98.5 99.5981 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 98.1238 99.7815 0 + vertex 97.0018 99.9982 0 + vertex 98.3151 99.6964 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 99.5441 98.5898 0 + vertex 97.0018 99.9982 0 + vertex 99.6488 98.4084 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 97.5209 99.9544 0 + vertex 97.0018 99.9982 0 + vertex 97.7258 99.9109 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 97.0018 99.9982 0 + vertex 97.927 99.8532 0 + vertex 97.7258 99.9109 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 97.0018 99.9982 0 + vertex 99.8838 97.8269 0 + vertex 99.8191 98.026 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 97.0018 99.9982 0 + vertex 97.5209 99.9544 0 + vertex 97.3136 99.9836 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 99.9708 97.4175 0 + vertex 99.9344 97.6237 0 + vertex 97.0018 99.9982 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 97.3136 99.9836 0 + vertex 97.1047 99.9982 0 + vertex 97.0018 99.9982 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 99.9927 97.2093 0 + vertex 97.0018 99.9982 0 + vertex 100 97 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -97.0018 99.9982 0 + vertex 97 100 0 + vertex -97 100 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 97.0018 99.9982 0 + vertex 97 100 0 + vertex -97.0018 99.9982 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 97.0018 99.9982 0 + vertex -97.0018 99.9982 0 + vertex 97.0018 -99.9982 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -97.0018 -99.9982 0 + vertex -100 97 0 + vertex -100 -97 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -97.1047 99.9982 0 + vertex -97.3136 99.9836 0 + vertex -97.0018 99.9982 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -97.3136 99.9836 0 + vertex -97.5209 99.9544 0 + vertex -97.0018 99.9982 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -97.5209 99.9544 0 + vertex -97.7258 99.9109 0 + vertex -97.0018 99.9982 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -97.7258 99.9109 0 + vertex -97.927 99.8532 0 + vertex -97.0018 99.9982 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -97.927 99.8532 0 + vertex -98.1238 99.7815 0 + vertex -97.0018 99.9982 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -98.1238 99.7815 0 + vertex -98.3151 99.6964 0 + vertex -97.0018 99.9982 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -98.3151 99.6964 0 + vertex -98.5 99.5981 0 + vertex -97.0018 99.9982 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -98.5 99.5981 0 + vertex -98.6776 99.4871 0 + vertex -97.0018 99.9982 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -98.6776 99.4871 0 + vertex -98.847 99.364 0 + vertex -97.0018 99.9982 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -98.847 99.364 0 + vertex -99.0074 99.2294 0 + vertex -97.0018 99.9982 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -99.0074 99.2294 0 + vertex -99.158 99.084 0 + vertex -97.0018 99.9982 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -99.158 99.084 0 + vertex -99.2981 98.9284 0 + vertex -97.0018 99.9982 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -99.2981 98.9284 0 + vertex -99.427 98.7634 0 + vertex -97.0018 99.9982 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -99.427 98.7634 0 + vertex -99.5441 98.5898 0 + vertex -97.0018 99.9982 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -99.5441 98.5898 0 + vertex -99.6488 98.4084 0 + vertex -97.0018 99.9982 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -99.6488 98.4084 0 + vertex -99.7406 98.2202 0 + vertex -97.0018 99.9982 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -99.7406 98.2202 0 + vertex -99.8191 98.026 0 + vertex -97.0018 99.9982 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -99.8191 98.026 0 + vertex -99.8838 97.8269 0 + vertex -97.0018 99.9982 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -99.8838 97.8269 0 + vertex -99.9344 97.6237 0 + vertex -97.0018 99.9982 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -99.9344 97.6237 0 + vertex -99.9708 97.4175 0 + vertex -97.0018 99.9982 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -99.9708 97.4175 0 + vertex -99.9927 97.2093 0 + vertex -97.0018 99.9982 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -99.9927 97.2093 0 + vertex -100 97 0 + vertex -97.0018 99.9982 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -97.0018 99.9982 0 + vertex -100 97 0 + vertex -97.0018 -99.9982 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -97.0018 99.9982 0 + vertex -97.0018 -99.9982 0 + vertex 97.0018 -99.9982 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -99.9927 -97.2093 0 + vertex -99.9708 -97.4175 0 + vertex -97.0018 -99.9982 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -97.0018 -99.9982 0 + vertex -99.9344 -97.6237 0 + vertex -99.8838 -97.8269 0 + endloop + endfacet + facet normal -0 -0 1 + outer loop + vertex -99.6488 -98.4084 0 + vertex -97.0018 -99.9982 0 + vertex -99.7406 -98.2202 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -97.0018 -99.9982 0 + vertex -99.8191 -98.026 0 + vertex -99.7406 -98.2202 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -99.158 -99.084 0 + vertex -99.0074 -99.2294 0 + vertex -97.0018 -99.9982 0 + endloop + endfacet + facet normal -0 -0 1 + outer loop + vertex -99.2981 -98.9284 0 + vertex -97.0018 -99.9982 0 + vertex -99.427 -98.7634 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -97.0018 -99.9982 0 + vertex -99.5441 -98.5898 0 + vertex -99.427 -98.7634 0 + endloop + endfacet + facet normal -0 -0 1 + outer loop + vertex -99.158 -99.084 0 + vertex -97.0018 -99.9982 0 + vertex -99.2981 -98.9284 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -98.1238 -99.7815 0 + vertex -97.927 -99.8532 0 + vertex -97.0018 -99.9982 0 + endloop + endfacet + facet normal -0 -0 1 + outer loop + vertex -98.6776 -99.4871 0 + vertex -97.0018 -99.9982 0 + vertex -98.847 -99.364 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -97.0018 -99.9982 0 + vertex -99.0074 -99.2294 0 + vertex -98.847 -99.364 0 + endloop + endfacet + facet normal -0 -0 1 + outer loop + vertex -98.5 -99.5981 0 + vertex -97.0018 -99.9982 0 + vertex -98.6776 -99.4871 0 + endloop + endfacet + facet normal -0 -0 1 + outer loop + vertex -98.3151 -99.6964 0 + vertex -97.0018 -99.9982 0 + vertex -98.5 -99.5981 0 + endloop + endfacet + facet normal -0 -0 1 + outer loop + vertex -98.1238 -99.7815 0 + vertex -97.0018 -99.9982 0 + vertex -98.3151 -99.6964 0 + endloop + endfacet + facet normal -0 -0 1 + outer loop + vertex -99.5441 -98.5898 0 + vertex -97.0018 -99.9982 0 + vertex -99.6488 -98.4084 0 + endloop + endfacet + facet normal -0 -0 1 + outer loop + vertex -97.5209 -99.9544 0 + vertex -97.0018 -99.9982 0 + vertex -97.7258 -99.9109 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -97.0018 -99.9982 0 + vertex -97.927 -99.8532 0 + vertex -97.7258 -99.9109 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -97.0018 -99.9982 0 + vertex -99.8838 -97.8269 0 + vertex -99.8191 -98.026 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -97.0018 -99.9982 0 + vertex -97.5209 -99.9544 0 + vertex -97.3136 -99.9836 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -99.9708 -97.4175 0 + vertex -99.9344 -97.6237 0 + vertex -97.0018 -99.9982 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -97.3136 -99.9836 0 + vertex -97.1047 -99.9982 0 + vertex -97.0018 -99.9982 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -97.0018 -99.9982 0 + vertex 97 -100 0 + vertex 97.0018 -99.9982 0 + endloop + endfacet + facet normal 0 -0 1 + outer loop + vertex -97 -100 0 + vertex 97 -100 0 + vertex -97.0018 -99.9982 0 + endloop + endfacet + facet normal -0 -0 1 + outer loop + vertex -99.9927 -97.2093 0 + vertex -97.0018 -99.9982 0 + vertex -100 -97 0 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex 97.1047 -99.9982 -3 + vertex 97.0018 -99.9982 0 + vertex 97.0018 -99.9982 -3 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex 97.1047 -99.9982 -3 + vertex 97.1047 -99.9982 0 + vertex 97.0018 -99.9982 0 + endloop + endfacet + facet normal 0.0697198 -0.997567 0 + outer loop + vertex 97.3136 -99.9836 -3 + vertex 97.1047 -99.9982 0 + vertex 97.1047 -99.9982 -3 + endloop + endfacet + facet normal 0.0697198 -0.997567 0 + outer loop + vertex 97.3136 -99.9836 -3 + vertex 97.3136 -99.9836 0 + vertex 97.1047 -99.9982 0 + endloop + endfacet + facet normal 0.139482 -0.990225 0 + outer loop + vertex 97.5209 -99.9544 -3 + vertex 97.3136 -99.9836 0 + vertex 97.3136 -99.9836 -3 + endloop + endfacet + facet normal 0.139482 -0.990225 0 + outer loop + vertex 97.5209 -99.9544 -3 + vertex 97.5209 -99.9544 0 + vertex 97.3136 -99.9836 0 + endloop + endfacet + facet normal 0.20767 -0.978199 0 + outer loop + vertex 97.7258 -99.9109 -3 + vertex 97.5209 -99.9544 0 + vertex 97.5209 -99.9544 -3 + endloop + endfacet + facet normal 0.20767 -0.978199 0 + outer loop + vertex 97.7258 -99.9109 -3 + vertex 97.7258 -99.9109 0 + vertex 97.5209 -99.9544 0 + endloop + endfacet + facet normal 0.275668 -0.961253 0 + outer loop + vertex 97.927 -99.8532 -3 + vertex 97.7258 -99.9109 0 + vertex 97.7258 -99.9109 -3 + endloop + endfacet + facet normal 0.275668 -0.961253 0 + outer loop + vertex 97.927 -99.8532 -3 + vertex 97.927 -99.8532 0 + vertex 97.7258 -99.9109 0 + endloop + endfacet + facet normal 0.342318 -0.939584 0 + outer loop + vertex 98.1238 -99.7815 -3 + vertex 97.927 -99.8532 0 + vertex 97.927 -99.8532 -3 + endloop + endfacet + facet normal 0.342318 -0.939584 0 + outer loop + vertex 98.1238 -99.7815 -3 + vertex 98.1238 -99.7815 0 + vertex 97.927 -99.8532 0 + endloop + endfacet + facet normal 0.406449 -0.913674 0 + outer loop + vertex 98.3151 -99.6964 -3 + vertex 98.1238 -99.7815 0 + vertex 98.1238 -99.7815 -3 + endloop + endfacet + facet normal 0.406449 -0.913674 0 + outer loop + vertex 98.3151 -99.6964 -3 + vertex 98.3151 -99.6964 0 + vertex 98.1238 -99.7815 0 + endloop + endfacet + facet normal 0.469423 -0.882973 0 + outer loop + vertex 98.5 -99.5981 -3 + vertex 98.3151 -99.6964 0 + vertex 98.3151 -99.6964 -3 + endloop + endfacet + facet normal 0.469423 -0.882973 0 + outer loop + vertex 98.5 -99.5981 -3 + vertex 98.5 -99.5981 0 + vertex 98.3151 -99.6964 0 + endloop + endfacet + facet normal 0.529999 -0.847998 0 + outer loop + vertex 98.6776 -99.4871 -3 + vertex 98.5 -99.5981 0 + vertex 98.5 -99.5981 -3 + endloop + endfacet + facet normal 0.529999 -0.847998 0 + outer loop + vertex 98.6776 -99.4871 -3 + vertex 98.6776 -99.4871 0 + vertex 98.5 -99.5981 0 + endloop + endfacet + facet normal 0.587859 -0.808963 0 + outer loop + vertex 98.847 -99.364 -3 + vertex 98.6776 -99.4871 0 + vertex 98.6776 -99.4871 -3 + endloop + endfacet + facet normal 0.587859 -0.808963 0 + outer loop + vertex 98.847 -99.364 -3 + vertex 98.847 -99.364 0 + vertex 98.6776 -99.4871 0 + endloop + endfacet + facet normal 0.642811 -0.766025 0 + outer loop + vertex 99.0074 -99.2294 -3 + vertex 98.847 -99.364 0 + vertex 98.847 -99.364 -3 + endloop + endfacet + facet normal 0.642811 -0.766025 0 + outer loop + vertex 99.0074 -99.2294 -3 + vertex 99.0074 -99.2294 0 + vertex 98.847 -99.364 0 + endloop + endfacet + facet normal 0.694577 -0.719418 0 + outer loop + vertex 99.158 -99.084 -3 + vertex 99.0074 -99.2294 0 + vertex 99.0074 -99.2294 -3 + endloop + endfacet + facet normal 0.694577 -0.719418 0 + outer loop + vertex 99.158 -99.084 -3 + vertex 99.158 -99.084 0 + vertex 99.0074 -99.2294 0 + endloop + endfacet + facet normal 0.743152 -0.669123 0 + outer loop + vertex 99.2981 -98.9284 -3 + vertex 99.158 -99.084 0 + vertex 99.158 -99.084 -3 + endloop + endfacet + facet normal 0.743152 -0.669123 0 + outer loop + vertex 99.2981 -98.9284 -3 + vertex 99.2981 -98.9284 0 + vertex 99.158 -99.084 0 + endloop + endfacet + facet normal 0.788039 -0.615626 0 + outer loop + vertex 99.427 -98.7634 -3 + vertex 99.2981 -98.9284 0 + vertex 99.2981 -98.9284 -3 + endloop + endfacet + facet normal 0.788039 -0.615626 0 + outer loop + vertex 99.427 -98.7634 -3 + vertex 99.427 -98.7634 0 + vertex 99.2981 -98.9284 0 + endloop + endfacet + facet normal 0.829026 -0.55921 0 + outer loop + vertex 99.5441 -98.5898 -3 + vertex 99.427 -98.7634 0 + vertex 99.427 -98.7634 -3 + endloop + endfacet + facet normal 0.829026 -0.55921 0 + outer loop + vertex 99.5441 -98.5898 -3 + vertex 99.5441 -98.5898 0 + vertex 99.427 -98.7634 0 + endloop + endfacet + facet normal 0.86609 -0.499888 0 + outer loop + vertex 99.6488 -98.4084 -3 + vertex 99.5441 -98.5898 0 + vertex 99.5441 -98.5898 -3 + endloop + endfacet + facet normal 0.86609 -0.499888 0 + outer loop + vertex 99.6488 -98.4084 -3 + vertex 99.6488 -98.4084 0 + vertex 99.5441 -98.5898 0 + endloop + endfacet + facet normal 0.898778 -0.438405 0 + outer loop + vertex 99.7406 -98.2202 -3 + vertex 99.6488 -98.4084 0 + vertex 99.6488 -98.4084 -3 + endloop + endfacet + facet normal 0.898778 -0.438405 0 + outer loop + vertex 99.7406 -98.2202 -3 + vertex 99.7406 -98.2202 0 + vertex 99.6488 -98.4084 0 + endloop + endfacet + facet normal 0.927121 -0.374763 0 + outer loop + vertex 99.8191 -98.026 -3 + vertex 99.7406 -98.2202 0 + vertex 99.7406 -98.2202 -3 + endloop + endfacet + facet normal 0.927121 -0.374763 0 + outer loop + vertex 99.8191 -98.026 -3 + vertex 99.8191 -98.026 0 + vertex 99.7406 -98.2202 0 + endloop + endfacet + facet normal 0.951045 -0.309054 0 + outer loop + vertex 99.8838 -97.8269 -3 + vertex 99.8191 -98.026 0 + vertex 99.8191 -98.026 -3 + endloop + endfacet + facet normal 0.951045 -0.309054 0 + outer loop + vertex 99.8838 -97.8269 -3 + vertex 99.8838 -97.8269 0 + vertex 99.8191 -98.026 0 + endloop + endfacet + facet normal 0.970367 -0.241637 0 + outer loop + vertex 99.9344 -97.6237 -3 + vertex 99.8838 -97.8269 0 + vertex 99.8838 -97.8269 -3 + endloop + endfacet + facet normal 0.970367 -0.241637 0 + outer loop + vertex 99.9344 -97.6237 -3 + vertex 99.9344 -97.6237 0 + vertex 99.8838 -97.8269 0 + endloop + endfacet + facet normal 0.984774 -0.17384 0 + outer loop + vertex 99.9708 -97.4175 -3 + vertex 99.9344 -97.6237 0 + vertex 99.9344 -97.6237 -3 + endloop + endfacet + facet normal 0.984774 -0.17384 0 + outer loop + vertex 99.9708 -97.4175 -3 + vertex 99.9708 -97.4175 0 + vertex 99.9344 -97.6237 0 + endloop + endfacet + facet normal 0.994513 -0.10461 0 + outer loop + vertex 99.9927 -97.2093 -3 + vertex 99.9708 -97.4175 0 + vertex 99.9708 -97.4175 -3 + endloop + endfacet + facet normal 0.994513 -0.10461 0 + outer loop + vertex 99.9927 -97.2093 -3 + vertex 99.9927 -97.2093 0 + vertex 99.9708 -97.4175 0 + endloop + endfacet + facet normal 0.999392 -0.034857 0 + outer loop + vertex 100 -97 -3 + vertex 99.9927 -97.2093 0 + vertex 99.9927 -97.2093 -3 + endloop + endfacet + facet normal 0.999392 -0.034857 0 + outer loop + vertex 100 -97 -3 + vertex 100 -97 0 + vertex 99.9927 -97.2093 0 + endloop + endfacet + facet normal 1 0 0 + outer loop + vertex 100 97 -3 + vertex 100 -97 0 + vertex 100 -97 -3 + endloop + endfacet + facet normal 1 0 -0 + outer loop + vertex 100 97 -3 + vertex 100 97 0 + vertex 100 -97 0 + endloop + endfacet + facet normal 0.999392 0.034857 0 + outer loop + vertex 99.9927 97.2093 -3 + vertex 100 97 0 + vertex 100 97 -3 + endloop + endfacet + facet normal 0.999392 0.034857 -0 + outer loop + vertex 99.9927 97.2093 -3 + vertex 99.9927 97.2093 0 + vertex 100 97 0 + endloop + endfacet + facet normal 0.994513 0.10461 0 + outer loop + vertex 99.9708 97.4175 -3 + vertex 99.9927 97.2093 0 + vertex 99.9927 97.2093 -3 + endloop + endfacet + facet normal 0.994513 0.10461 -0 + outer loop + vertex 99.9708 97.4175 -3 + vertex 99.9708 97.4175 0 + vertex 99.9927 97.2093 0 + endloop + endfacet + facet normal 0.984774 0.17384 0 + outer loop + vertex 99.9344 97.6237 -3 + vertex 99.9708 97.4175 0 + vertex 99.9708 97.4175 -3 + endloop + endfacet + facet normal 0.984774 0.17384 -0 + outer loop + vertex 99.9344 97.6237 -3 + vertex 99.9344 97.6237 0 + vertex 99.9708 97.4175 0 + endloop + endfacet + facet normal 0.970367 0.241637 0 + outer loop + vertex 99.8838 97.8269 -3 + vertex 99.9344 97.6237 0 + vertex 99.9344 97.6237 -3 + endloop + endfacet + facet normal 0.970367 0.241637 -0 + outer loop + vertex 99.8838 97.8269 -3 + vertex 99.8838 97.8269 0 + vertex 99.9344 97.6237 0 + endloop + endfacet + facet normal 0.951045 0.309054 0 + outer loop + vertex 99.8191 98.026 -3 + vertex 99.8838 97.8269 0 + vertex 99.8838 97.8269 -3 + endloop + endfacet + facet normal 0.951045 0.309054 -0 + outer loop + vertex 99.8191 98.026 -3 + vertex 99.8191 98.026 0 + vertex 99.8838 97.8269 0 + endloop + endfacet + facet normal 0.927121 0.374763 0 + outer loop + vertex 99.7406 98.2202 -3 + vertex 99.8191 98.026 0 + vertex 99.8191 98.026 -3 + endloop + endfacet + facet normal 0.927121 0.374763 -0 + outer loop + vertex 99.7406 98.2202 -3 + vertex 99.7406 98.2202 0 + vertex 99.8191 98.026 0 + endloop + endfacet + facet normal 0.898778 0.438405 0 + outer loop + vertex 99.6488 98.4084 -3 + vertex 99.7406 98.2202 0 + vertex 99.7406 98.2202 -3 + endloop + endfacet + facet normal 0.898778 0.438405 -0 + outer loop + vertex 99.6488 98.4084 -3 + vertex 99.6488 98.4084 0 + vertex 99.7406 98.2202 0 + endloop + endfacet + facet normal 0.86609 0.499888 0 + outer loop + vertex 99.5441 98.5898 -3 + vertex 99.6488 98.4084 0 + vertex 99.6488 98.4084 -3 + endloop + endfacet + facet normal 0.86609 0.499888 -0 + outer loop + vertex 99.5441 98.5898 -3 + vertex 99.5441 98.5898 0 + vertex 99.6488 98.4084 0 + endloop + endfacet + facet normal 0.829026 0.55921 0 + outer loop + vertex 99.427 98.7634 -3 + vertex 99.5441 98.5898 0 + vertex 99.5441 98.5898 -3 + endloop + endfacet + facet normal 0.829026 0.55921 -0 + outer loop + vertex 99.427 98.7634 -3 + vertex 99.427 98.7634 0 + vertex 99.5441 98.5898 0 + endloop + endfacet + facet normal 0.788039 0.615626 0 + outer loop + vertex 99.2981 98.9284 -3 + vertex 99.427 98.7634 0 + vertex 99.427 98.7634 -3 + endloop + endfacet + facet normal 0.788039 0.615626 -0 + outer loop + vertex 99.2981 98.9284 -3 + vertex 99.2981 98.9284 0 + vertex 99.427 98.7634 0 + endloop + endfacet + facet normal 0.743152 0.669123 0 + outer loop + vertex 99.158 99.084 -3 + vertex 99.2981 98.9284 0 + vertex 99.2981 98.9284 -3 + endloop + endfacet + facet normal 0.743152 0.669123 -0 + outer loop + vertex 99.158 99.084 -3 + vertex 99.158 99.084 0 + vertex 99.2981 98.9284 0 + endloop + endfacet + facet normal 0.694577 0.719418 0 + outer loop + vertex 99.0074 99.2294 -3 + vertex 99.158 99.084 0 + vertex 99.158 99.084 -3 + endloop + endfacet + facet normal 0.694577 0.719418 -0 + outer loop + vertex 99.0074 99.2294 -3 + vertex 99.0074 99.2294 0 + vertex 99.158 99.084 0 + endloop + endfacet + facet normal 0.642811 0.766025 0 + outer loop + vertex 98.847 99.364 -3 + vertex 99.0074 99.2294 0 + vertex 99.0074 99.2294 -3 + endloop + endfacet + facet normal 0.642811 0.766025 -0 + outer loop + vertex 98.847 99.364 -3 + vertex 98.847 99.364 0 + vertex 99.0074 99.2294 0 + endloop + endfacet + facet normal 0.587859 0.808963 0 + outer loop + vertex 98.6776 99.4871 -3 + vertex 98.847 99.364 0 + vertex 98.847 99.364 -3 + endloop + endfacet + facet normal 0.587859 0.808963 -0 + outer loop + vertex 98.6776 99.4871 -3 + vertex 98.6776 99.4871 0 + vertex 98.847 99.364 0 + endloop + endfacet + facet normal 0.529999 0.847998 0 + outer loop + vertex 98.5 99.5981 -3 + vertex 98.6776 99.4871 0 + vertex 98.6776 99.4871 -3 + endloop + endfacet + facet normal 0.529999 0.847998 -0 + outer loop + vertex 98.5 99.5981 -3 + vertex 98.5 99.5981 0 + vertex 98.6776 99.4871 0 + endloop + endfacet + facet normal 0.469423 0.882973 0 + outer loop + vertex 98.3151 99.6964 -3 + vertex 98.5 99.5981 0 + vertex 98.5 99.5981 -3 + endloop + endfacet + facet normal 0.469423 0.882973 -0 + outer loop + vertex 98.3151 99.6964 -3 + vertex 98.3151 99.6964 0 + vertex 98.5 99.5981 0 + endloop + endfacet + facet normal 0.406449 0.913674 0 + outer loop + vertex 98.1238 99.7815 -3 + vertex 98.3151 99.6964 0 + vertex 98.3151 99.6964 -3 + endloop + endfacet + facet normal 0.406449 0.913674 -0 + outer loop + vertex 98.1238 99.7815 -3 + vertex 98.1238 99.7815 0 + vertex 98.3151 99.6964 0 + endloop + endfacet + facet normal 0.342318 0.939584 0 + outer loop + vertex 97.927 99.8532 -3 + vertex 98.1238 99.7815 0 + vertex 98.1238 99.7815 -3 + endloop + endfacet + facet normal 0.342318 0.939584 -0 + outer loop + vertex 97.927 99.8532 -3 + vertex 97.927 99.8532 0 + vertex 98.1238 99.7815 0 + endloop + endfacet + facet normal 0.275668 0.961253 0 + outer loop + vertex 97.7258 99.9109 -3 + vertex 97.927 99.8532 0 + vertex 97.927 99.8532 -3 + endloop + endfacet + facet normal 0.275668 0.961253 -0 + outer loop + vertex 97.7258 99.9109 -3 + vertex 97.7258 99.9109 0 + vertex 97.927 99.8532 0 + endloop + endfacet + facet normal 0.20767 0.978199 0 + outer loop + vertex 97.5209 99.9544 -3 + vertex 97.7258 99.9109 0 + vertex 97.7258 99.9109 -3 + endloop + endfacet + facet normal 0.20767 0.978199 -0 + outer loop + vertex 97.5209 99.9544 -3 + vertex 97.5209 99.9544 0 + vertex 97.7258 99.9109 0 + endloop + endfacet + facet normal 0.139482 0.990225 0 + outer loop + vertex 97.3136 99.9836 -3 + vertex 97.5209 99.9544 0 + vertex 97.5209 99.9544 -3 + endloop + endfacet + facet normal 0.139482 0.990225 -0 + outer loop + vertex 97.3136 99.9836 -3 + vertex 97.3136 99.9836 0 + vertex 97.5209 99.9544 0 + endloop + endfacet + facet normal 0.0697198 0.997567 0 + outer loop + vertex 97.1047 99.9982 -3 + vertex 97.3136 99.9836 0 + vertex 97.3136 99.9836 -3 + endloop + endfacet + facet normal 0.0697198 0.997567 -0 + outer loop + vertex 97.1047 99.9982 -3 + vertex 97.1047 99.9982 0 + vertex 97.3136 99.9836 0 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex 97.0018 99.9982 -3 + vertex 97.1047 99.9982 0 + vertex 97.1047 99.9982 -3 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex 97.0018 99.9982 -3 + vertex 97.0018 99.9982 0 + vertex 97.1047 99.9982 0 + endloop + endfacet + facet normal 0.707107 0.707107 0 + outer loop + vertex 97 100 -3 + vertex 97.0018 99.9982 0 + vertex 97.0018 99.9982 -3 + endloop + endfacet + facet normal 0.707107 0.707107 -0 + outer loop + vertex 97 100 -3 + vertex 97 100 0 + vertex 97.0018 99.9982 0 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex -97 100 -3 + vertex 97 100 0 + vertex 97 100 -3 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex -97 100 -3 + vertex -97 100 0 + vertex 97 100 0 + endloop + endfacet + facet normal -0.707107 0.707107 0 + outer loop + vertex -97.0018 99.9982 -3 + vertex -97 100 0 + vertex -97 100 -3 + endloop + endfacet + facet normal -0.707107 0.707107 0 + outer loop + vertex -97.0018 99.9982 -3 + vertex -97.0018 99.9982 0 + vertex -97 100 0 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex -97.1047 99.9982 -3 + vertex -97.0018 99.9982 0 + vertex -97.0018 99.9982 -3 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex -97.1047 99.9982 -3 + vertex -97.1047 99.9982 0 + vertex -97.0018 99.9982 0 + endloop + endfacet + facet normal -0.0697198 0.997567 0 + outer loop + vertex -97.3136 99.9836 -3 + vertex -97.1047 99.9982 0 + vertex -97.1047 99.9982 -3 + endloop + endfacet + facet normal -0.0697198 0.997567 0 + outer loop + vertex -97.3136 99.9836 -3 + vertex -97.3136 99.9836 0 + vertex -97.1047 99.9982 0 + endloop + endfacet + facet normal -0.139482 0.990225 0 + outer loop + vertex -97.5209 99.9544 -3 + vertex -97.3136 99.9836 0 + vertex -97.3136 99.9836 -3 + endloop + endfacet + facet normal -0.139482 0.990225 0 + outer loop + vertex -97.5209 99.9544 -3 + vertex -97.5209 99.9544 0 + vertex -97.3136 99.9836 0 + endloop + endfacet + facet normal -0.20767 0.978199 0 + outer loop + vertex -97.7258 99.9109 -3 + vertex -97.5209 99.9544 0 + vertex -97.5209 99.9544 -3 + endloop + endfacet + facet normal -0.20767 0.978199 0 + outer loop + vertex -97.7258 99.9109 -3 + vertex -97.7258 99.9109 0 + vertex -97.5209 99.9544 0 + endloop + endfacet + facet normal -0.275668 0.961253 0 + outer loop + vertex -97.927 99.8532 -3 + vertex -97.7258 99.9109 0 + vertex -97.7258 99.9109 -3 + endloop + endfacet + facet normal -0.275668 0.961253 0 + outer loop + vertex -97.927 99.8532 -3 + vertex -97.927 99.8532 0 + vertex -97.7258 99.9109 0 + endloop + endfacet + facet normal -0.342318 0.939584 0 + outer loop + vertex -98.1238 99.7815 -3 + vertex -97.927 99.8532 0 + vertex -97.927 99.8532 -3 + endloop + endfacet + facet normal -0.342318 0.939584 0 + outer loop + vertex -98.1238 99.7815 -3 + vertex -98.1238 99.7815 0 + vertex -97.927 99.8532 0 + endloop + endfacet + facet normal -0.406449 0.913674 0 + outer loop + vertex -98.3151 99.6964 -3 + vertex -98.1238 99.7815 0 + vertex -98.1238 99.7815 -3 + endloop + endfacet + facet normal -0.406449 0.913674 0 + outer loop + vertex -98.3151 99.6964 -3 + vertex -98.3151 99.6964 0 + vertex -98.1238 99.7815 0 + endloop + endfacet + facet normal -0.469423 0.882973 0 + outer loop + vertex -98.5 99.5981 -3 + vertex -98.3151 99.6964 0 + vertex -98.3151 99.6964 -3 + endloop + endfacet + facet normal -0.469423 0.882973 0 + outer loop + vertex -98.5 99.5981 -3 + vertex -98.5 99.5981 0 + vertex -98.3151 99.6964 0 + endloop + endfacet + facet normal -0.529999 0.847998 0 + outer loop + vertex -98.6776 99.4871 -3 + vertex -98.5 99.5981 0 + vertex -98.5 99.5981 -3 + endloop + endfacet + facet normal -0.529999 0.847998 0 + outer loop + vertex -98.6776 99.4871 -3 + vertex -98.6776 99.4871 0 + vertex -98.5 99.5981 0 + endloop + endfacet + facet normal -0.587859 0.808963 0 + outer loop + vertex -98.847 99.364 -3 + vertex -98.6776 99.4871 0 + vertex -98.6776 99.4871 -3 + endloop + endfacet + facet normal -0.587859 0.808963 0 + outer loop + vertex -98.847 99.364 -3 + vertex -98.847 99.364 0 + vertex -98.6776 99.4871 0 + endloop + endfacet + facet normal -0.642811 0.766025 0 + outer loop + vertex -99.0074 99.2294 -3 + vertex -98.847 99.364 0 + vertex -98.847 99.364 -3 + endloop + endfacet + facet normal -0.642811 0.766025 0 + outer loop + vertex -99.0074 99.2294 -3 + vertex -99.0074 99.2294 0 + vertex -98.847 99.364 0 + endloop + endfacet + facet normal -0.694577 0.719418 0 + outer loop + vertex -99.158 99.084 -3 + vertex -99.0074 99.2294 0 + vertex -99.0074 99.2294 -3 + endloop + endfacet + facet normal -0.694577 0.719418 0 + outer loop + vertex -99.158 99.084 -3 + vertex -99.158 99.084 0 + vertex -99.0074 99.2294 0 + endloop + endfacet + facet normal -0.743152 0.669123 0 + outer loop + vertex -99.2981 98.9284 -3 + vertex -99.158 99.084 0 + vertex -99.158 99.084 -3 + endloop + endfacet + facet normal -0.743152 0.669123 0 + outer loop + vertex -99.2981 98.9284 -3 + vertex -99.2981 98.9284 0 + vertex -99.158 99.084 0 + endloop + endfacet + facet normal -0.788039 0.615626 0 + outer loop + vertex -99.427 98.7634 -3 + vertex -99.2981 98.9284 0 + vertex -99.2981 98.9284 -3 + endloop + endfacet + facet normal -0.788039 0.615626 0 + outer loop + vertex -99.427 98.7634 -3 + vertex -99.427 98.7634 0 + vertex -99.2981 98.9284 0 + endloop + endfacet + facet normal -0.829026 0.55921 0 + outer loop + vertex -99.5441 98.5898 -3 + vertex -99.427 98.7634 0 + vertex -99.427 98.7634 -3 + endloop + endfacet + facet normal -0.829026 0.55921 0 + outer loop + vertex -99.5441 98.5898 -3 + vertex -99.5441 98.5898 0 + vertex -99.427 98.7634 0 + endloop + endfacet + facet normal -0.86609 0.499888 0 + outer loop + vertex -99.6488 98.4084 -3 + vertex -99.5441 98.5898 0 + vertex -99.5441 98.5898 -3 + endloop + endfacet + facet normal -0.86609 0.499888 0 + outer loop + vertex -99.6488 98.4084 -3 + vertex -99.6488 98.4084 0 + vertex -99.5441 98.5898 0 + endloop + endfacet + facet normal -0.898778 0.438405 0 + outer loop + vertex -99.7406 98.2202 -3 + vertex -99.6488 98.4084 0 + vertex -99.6488 98.4084 -3 + endloop + endfacet + facet normal -0.898778 0.438405 0 + outer loop + vertex -99.7406 98.2202 -3 + vertex -99.7406 98.2202 0 + vertex -99.6488 98.4084 0 + endloop + endfacet + facet normal -0.927121 0.374763 0 + outer loop + vertex -99.8191 98.026 -3 + vertex -99.7406 98.2202 0 + vertex -99.7406 98.2202 -3 + endloop + endfacet + facet normal -0.927121 0.374763 0 + outer loop + vertex -99.8191 98.026 -3 + vertex -99.8191 98.026 0 + vertex -99.7406 98.2202 0 + endloop + endfacet + facet normal -0.951045 0.309054 0 + outer loop + vertex -99.8838 97.8269 -3 + vertex -99.8191 98.026 0 + vertex -99.8191 98.026 -3 + endloop + endfacet + facet normal -0.951045 0.309054 0 + outer loop + vertex -99.8838 97.8269 -3 + vertex -99.8838 97.8269 0 + vertex -99.8191 98.026 0 + endloop + endfacet + facet normal -0.970367 0.241637 0 + outer loop + vertex -99.9344 97.6237 -3 + vertex -99.8838 97.8269 0 + vertex -99.8838 97.8269 -3 + endloop + endfacet + facet normal -0.970367 0.241637 0 + outer loop + vertex -99.9344 97.6237 -3 + vertex -99.9344 97.6237 0 + vertex -99.8838 97.8269 0 + endloop + endfacet + facet normal -0.984774 0.17384 0 + outer loop + vertex -99.9708 97.4175 -3 + vertex -99.9344 97.6237 0 + vertex -99.9344 97.6237 -3 + endloop + endfacet + facet normal -0.984774 0.17384 0 + outer loop + vertex -99.9708 97.4175 -3 + vertex -99.9708 97.4175 0 + vertex -99.9344 97.6237 0 + endloop + endfacet + facet normal -0.994513 0.10461 0 + outer loop + vertex -99.9927 97.2093 -3 + vertex -99.9708 97.4175 0 + vertex -99.9708 97.4175 -3 + endloop + endfacet + facet normal -0.994513 0.10461 0 + outer loop + vertex -99.9927 97.2093 -3 + vertex -99.9927 97.2093 0 + vertex -99.9708 97.4175 0 + endloop + endfacet + facet normal -0.999392 0.034857 0 + outer loop + vertex -100 97 -3 + vertex -99.9927 97.2093 0 + vertex -99.9927 97.2093 -3 + endloop + endfacet + facet normal -0.999392 0.034857 0 + outer loop + vertex -100 97 -3 + vertex -100 97 0 + vertex -99.9927 97.2093 0 + endloop + endfacet + facet normal -1 0 0 + outer loop + vertex -100 -97 -3 + vertex -100 97 0 + vertex -100 97 -3 + endloop + endfacet + facet normal -1 0 0 + outer loop + vertex -100 -97 -3 + vertex -100 -97 0 + vertex -100 97 0 + endloop + endfacet + facet normal -0.999392 -0.034857 0 + outer loop + vertex -99.9927 -97.2093 -3 + vertex -100 -97 0 + vertex -100 -97 -3 + endloop + endfacet + facet normal -0.999392 -0.034857 0 + outer loop + vertex -99.9927 -97.2093 -3 + vertex -99.9927 -97.2093 0 + vertex -100 -97 0 + endloop + endfacet + facet normal -0.994513 -0.10461 0 + outer loop + vertex -99.9708 -97.4175 -3 + vertex -99.9927 -97.2093 0 + vertex -99.9927 -97.2093 -3 + endloop + endfacet + facet normal -0.994513 -0.10461 0 + outer loop + vertex -99.9708 -97.4175 -3 + vertex -99.9708 -97.4175 0 + vertex -99.9927 -97.2093 0 + endloop + endfacet + facet normal -0.984774 -0.17384 0 + outer loop + vertex -99.9344 -97.6237 -3 + vertex -99.9708 -97.4175 0 + vertex -99.9708 -97.4175 -3 + endloop + endfacet + facet normal -0.984774 -0.17384 0 + outer loop + vertex -99.9344 -97.6237 -3 + vertex -99.9344 -97.6237 0 + vertex -99.9708 -97.4175 0 + endloop + endfacet + facet normal -0.970367 -0.241637 0 + outer loop + vertex -99.8838 -97.8269 -3 + vertex -99.9344 -97.6237 0 + vertex -99.9344 -97.6237 -3 + endloop + endfacet + facet normal -0.970367 -0.241637 0 + outer loop + vertex -99.8838 -97.8269 -3 + vertex -99.8838 -97.8269 0 + vertex -99.9344 -97.6237 0 + endloop + endfacet + facet normal -0.951045 -0.309054 0 + outer loop + vertex -99.8191 -98.026 -3 + vertex -99.8838 -97.8269 0 + vertex -99.8838 -97.8269 -3 + endloop + endfacet + facet normal -0.951045 -0.309054 0 + outer loop + vertex -99.8191 -98.026 -3 + vertex -99.8191 -98.026 0 + vertex -99.8838 -97.8269 0 + endloop + endfacet + facet normal -0.927121 -0.374763 0 + outer loop + vertex -99.7406 -98.2202 -3 + vertex -99.8191 -98.026 0 + vertex -99.8191 -98.026 -3 + endloop + endfacet + facet normal -0.927121 -0.374763 0 + outer loop + vertex -99.7406 -98.2202 -3 + vertex -99.7406 -98.2202 0 + vertex -99.8191 -98.026 0 + endloop + endfacet + facet normal -0.898778 -0.438405 0 + outer loop + vertex -99.6488 -98.4084 -3 + vertex -99.7406 -98.2202 0 + vertex -99.7406 -98.2202 -3 + endloop + endfacet + facet normal -0.898778 -0.438405 0 + outer loop + vertex -99.6488 -98.4084 -3 + vertex -99.6488 -98.4084 0 + vertex -99.7406 -98.2202 0 + endloop + endfacet + facet normal -0.86609 -0.499888 0 + outer loop + vertex -99.5441 -98.5898 -3 + vertex -99.6488 -98.4084 0 + vertex -99.6488 -98.4084 -3 + endloop + endfacet + facet normal -0.86609 -0.499888 0 + outer loop + vertex -99.5441 -98.5898 -3 + vertex -99.5441 -98.5898 0 + vertex -99.6488 -98.4084 0 + endloop + endfacet + facet normal -0.829026 -0.55921 0 + outer loop + vertex -99.427 -98.7634 -3 + vertex -99.5441 -98.5898 0 + vertex -99.5441 -98.5898 -3 + endloop + endfacet + facet normal -0.829026 -0.55921 0 + outer loop + vertex -99.427 -98.7634 -3 + vertex -99.427 -98.7634 0 + vertex -99.5441 -98.5898 0 + endloop + endfacet + facet normal -0.788039 -0.615626 0 + outer loop + vertex -99.2981 -98.9284 -3 + vertex -99.427 -98.7634 0 + vertex -99.427 -98.7634 -3 + endloop + endfacet + facet normal -0.788039 -0.615626 0 + outer loop + vertex -99.2981 -98.9284 -3 + vertex -99.2981 -98.9284 0 + vertex -99.427 -98.7634 0 + endloop + endfacet + facet normal -0.743152 -0.669123 0 + outer loop + vertex -99.158 -99.084 -3 + vertex -99.2981 -98.9284 0 + vertex -99.2981 -98.9284 -3 + endloop + endfacet + facet normal -0.743152 -0.669123 0 + outer loop + vertex -99.158 -99.084 -3 + vertex -99.158 -99.084 0 + vertex -99.2981 -98.9284 0 + endloop + endfacet + facet normal -0.694577 -0.719418 0 + outer loop + vertex -99.0074 -99.2294 -3 + vertex -99.158 -99.084 0 + vertex -99.158 -99.084 -3 + endloop + endfacet + facet normal -0.694577 -0.719418 0 + outer loop + vertex -99.0074 -99.2294 -3 + vertex -99.0074 -99.2294 0 + vertex -99.158 -99.084 0 + endloop + endfacet + facet normal -0.642811 -0.766025 0 + outer loop + vertex -98.847 -99.364 -3 + vertex -99.0074 -99.2294 0 + vertex -99.0074 -99.2294 -3 + endloop + endfacet + facet normal -0.642811 -0.766025 0 + outer loop + vertex -98.847 -99.364 -3 + vertex -98.847 -99.364 0 + vertex -99.0074 -99.2294 0 + endloop + endfacet + facet normal -0.587859 -0.808963 0 + outer loop + vertex -98.6776 -99.4871 -3 + vertex -98.847 -99.364 0 + vertex -98.847 -99.364 -3 + endloop + endfacet + facet normal -0.587859 -0.808963 0 + outer loop + vertex -98.6776 -99.4871 -3 + vertex -98.6776 -99.4871 0 + vertex -98.847 -99.364 0 + endloop + endfacet + facet normal -0.529999 -0.847998 0 + outer loop + vertex -98.5 -99.5981 -3 + vertex -98.6776 -99.4871 0 + vertex -98.6776 -99.4871 -3 + endloop + endfacet + facet normal -0.529999 -0.847998 0 + outer loop + vertex -98.5 -99.5981 -3 + vertex -98.5 -99.5981 0 + vertex -98.6776 -99.4871 0 + endloop + endfacet + facet normal -0.469423 -0.882973 0 + outer loop + vertex -98.3151 -99.6964 -3 + vertex -98.5 -99.5981 0 + vertex -98.5 -99.5981 -3 + endloop + endfacet + facet normal -0.469423 -0.882973 0 + outer loop + vertex -98.3151 -99.6964 -3 + vertex -98.3151 -99.6964 0 + vertex -98.5 -99.5981 0 + endloop + endfacet + facet normal -0.406449 -0.913674 0 + outer loop + vertex -98.1238 -99.7815 -3 + vertex -98.3151 -99.6964 0 + vertex -98.3151 -99.6964 -3 + endloop + endfacet + facet normal -0.406449 -0.913674 0 + outer loop + vertex -98.1238 -99.7815 -3 + vertex -98.1238 -99.7815 0 + vertex -98.3151 -99.6964 0 + endloop + endfacet + facet normal -0.342318 -0.939584 0 + outer loop + vertex -97.927 -99.8532 -3 + vertex -98.1238 -99.7815 0 + vertex -98.1238 -99.7815 -3 + endloop + endfacet + facet normal -0.342318 -0.939584 0 + outer loop + vertex -97.927 -99.8532 -3 + vertex -97.927 -99.8532 0 + vertex -98.1238 -99.7815 0 + endloop + endfacet + facet normal -0.275668 -0.961253 0 + outer loop + vertex -97.7258 -99.9109 -3 + vertex -97.927 -99.8532 0 + vertex -97.927 -99.8532 -3 + endloop + endfacet + facet normal -0.275668 -0.961253 0 + outer loop + vertex -97.7258 -99.9109 -3 + vertex -97.7258 -99.9109 0 + vertex -97.927 -99.8532 0 + endloop + endfacet + facet normal -0.20767 -0.978199 0 + outer loop + vertex -97.5209 -99.9544 -3 + vertex -97.7258 -99.9109 0 + vertex -97.7258 -99.9109 -3 + endloop + endfacet + facet normal -0.20767 -0.978199 0 + outer loop + vertex -97.5209 -99.9544 -3 + vertex -97.5209 -99.9544 0 + vertex -97.7258 -99.9109 0 + endloop + endfacet + facet normal -0.139482 -0.990225 0 + outer loop + vertex -97.3136 -99.9836 -3 + vertex -97.5209 -99.9544 0 + vertex -97.5209 -99.9544 -3 + endloop + endfacet + facet normal -0.139482 -0.990225 0 + outer loop + vertex -97.3136 -99.9836 -3 + vertex -97.3136 -99.9836 0 + vertex -97.5209 -99.9544 0 + endloop + endfacet + facet normal -0.0697198 -0.997567 0 + outer loop + vertex -97.1047 -99.9982 -3 + vertex -97.3136 -99.9836 0 + vertex -97.3136 -99.9836 -3 + endloop + endfacet + facet normal -0.0697198 -0.997567 0 + outer loop + vertex -97.1047 -99.9982 -3 + vertex -97.1047 -99.9982 0 + vertex -97.3136 -99.9836 0 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex -97.0018 -99.9982 -3 + vertex -97.1047 -99.9982 0 + vertex -97.1047 -99.9982 -3 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex -97.0018 -99.9982 -3 + vertex -97.0018 -99.9982 0 + vertex -97.1047 -99.9982 0 + endloop + endfacet + facet normal -0.707107 -0.707107 0 + outer loop + vertex -97 -100 -3 + vertex -97.0018 -99.9982 0 + vertex -97.0018 -99.9982 -3 + endloop + endfacet + facet normal -0.707107 -0.707107 0 + outer loop + vertex -97 -100 -3 + vertex -97 -100 0 + vertex -97.0018 -99.9982 0 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex 97 -100 -3 + vertex -97 -100 0 + vertex -97 -100 -3 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex 97 -100 -3 + vertex 97 -100 0 + vertex -97 -100 0 + endloop + endfacet + facet normal 0.707107 -0.707107 0 + outer loop + vertex 97.0018 -99.9982 -3 + vertex 97 -100 0 + vertex 97 -100 -3 + endloop + endfacet + facet normal 0.707107 -0.707107 0 + outer loop + vertex 97.0018 -99.9982 -3 + vertex 97.0018 -99.9982 0 + vertex 97 -100 0 + endloop + endfacet +endsolid OpenSCAD_Model From e5b7b20be1dc0fe827e451633d4d9a506ed775e3 Mon Sep 17 00:00:00 2001 From: Pascal de Bruijn Date: Sun, 28 Feb 2021 16:57:35 +0100 Subject: [PATCH 020/285] creality.ini: CR-5 Pro more accurate bed --- resources/profiles/Creality.ini | 6 +- resources/profiles/Creality/cr5pro.svg | 4 + resources/profiles/Creality/cr5pro_bed.stl | 2774 ++++++++++++++++++++ 3 files changed, 2781 insertions(+), 3 deletions(-) create mode 100644 resources/profiles/Creality/cr5pro.svg create mode 100644 resources/profiles/Creality/cr5pro_bed.stl diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index 2e9971f21f..de6f103a0e 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -100,8 +100,8 @@ default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @ #variants = 0.4 #technology = FFF #family = CR -#bed_model = cr10mini_bed.stl -#bed_texture = cr10mini.svg +#bed_model = cr5pro_bed.stl +#bed_texture = cr5pro.svg #default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY #[printer_model:CR6SE] @@ -905,7 +905,7 @@ printer_notes = Don't remove the following keywords! These keywords are used in #[printer:Creality CR-5 Pro] #inherits = *common*; *slowabl*; *descendingz* #retract_length = 3 -#bed_shape = 2.5x5,302.5x5,302.5x225,2.5x225 +#bed_shape = 5x5,295x5,295x215,5x215 #max_print_height = 380 #printer_model = CR5PRO #printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR5PRO\nPRINTER_HAS_BOWDEN diff --git a/resources/profiles/Creality/cr5pro.svg b/resources/profiles/Creality/cr5pro.svg new file mode 100644 index 0000000000..7f44fd6020 --- /dev/null +++ b/resources/profiles/Creality/cr5pro.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/profiles/Creality/cr5pro_bed.stl b/resources/profiles/Creality/cr5pro_bed.stl new file mode 100644 index 0000000000..b3e13687bb --- /dev/null +++ b/resources/profiles/Creality/cr5pro_bed.stl @@ -0,0 +1,2774 @@ +solid OpenSCAD_Model + facet normal 0 0 -1 + outer loop + vertex 147.105 -112.498 -3 + vertex 147.002 -112.498 -3 + vertex 147.314 -112.484 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 147.314 -112.484 -3 + vertex 147.002 -112.498 -3 + vertex 147.521 -112.454 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 150 -109.5 -3 + vertex 147.002 -112.498 -3 + vertex 147.002 112.498 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 147.521 -112.454 -3 + vertex 147.002 -112.498 -3 + vertex 147.726 -112.411 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 147.726 -112.411 -3 + vertex 147.002 -112.498 -3 + vertex 147.927 -112.353 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 147.927 -112.353 -3 + vertex 147.002 -112.498 -3 + vertex 148.124 -112.282 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 148.124 -112.282 -3 + vertex 147.002 -112.498 -3 + vertex 148.315 -112.196 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 148.315 -112.196 -3 + vertex 147.002 -112.498 -3 + vertex 148.5 -112.098 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 148.5 -112.098 -3 + vertex 147.002 -112.498 -3 + vertex 148.678 -111.987 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 148.678 -111.987 -3 + vertex 147.002 -112.498 -3 + vertex 148.847 -111.864 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 148.847 -111.864 -3 + vertex 147.002 -112.498 -3 + vertex 149.007 -111.729 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 149.007 -111.729 -3 + vertex 147.002 -112.498 -3 + vertex 149.158 -111.584 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 149.158 -111.584 -3 + vertex 147.002 -112.498 -3 + vertex 149.298 -111.428 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 149.298 -111.428 -3 + vertex 147.002 -112.498 -3 + vertex 149.427 -111.263 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 149.427 -111.263 -3 + vertex 147.002 -112.498 -3 + vertex 149.544 -111.09 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 149.544 -111.09 -3 + vertex 147.002 -112.498 -3 + vertex 149.649 -110.908 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 149.649 -110.908 -3 + vertex 147.002 -112.498 -3 + vertex 149.741 -110.72 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 149.741 -110.72 -3 + vertex 147.002 -112.498 -3 + vertex 149.819 -110.526 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 149.819 -110.526 -3 + vertex 147.002 -112.498 -3 + vertex 149.884 -110.327 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 149.884 -110.327 -3 + vertex 147.002 -112.498 -3 + vertex 149.934 -110.124 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 149.934 -110.124 -3 + vertex 147.002 -112.498 -3 + vertex 149.971 -109.918 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 149.971 -109.918 -3 + vertex 147.002 -112.498 -3 + vertex 149.993 -109.709 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 149.993 -109.709 -3 + vertex 147.002 -112.498 -3 + vertex 150 -109.5 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 150 -109.5 -3 + vertex 147.002 112.498 -3 + vertex 150 109.5 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 147.002 112.498 -3 + vertex 149.971 109.918 -3 + vertex 149.993 109.709 -3 + endloop + endfacet + facet normal -0 -0 -1 + outer loop + vertex 149.884 110.327 -3 + vertex 149.934 110.124 -3 + vertex 147.002 112.498 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 149.741 110.72 -3 + vertex 147.002 112.498 -3 + vertex 149.649 110.908 -3 + endloop + endfacet + facet normal -0 -0 -1 + outer loop + vertex 149.741 110.72 -3 + vertex 149.819 110.526 -3 + vertex 147.002 112.498 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 147.002 112.498 -3 + vertex 149.007 111.729 -3 + vertex 149.158 111.584 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 149.427 111.263 -3 + vertex 147.002 112.498 -3 + vertex 149.298 111.428 -3 + endloop + endfacet + facet normal -0 -0 -1 + outer loop + vertex 149.427 111.263 -3 + vertex 149.544 111.09 -3 + vertex 147.002 112.498 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 149.298 111.428 -3 + vertex 147.002 112.498 -3 + vertex 149.158 111.584 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 147.002 112.498 -3 + vertex 147.927 112.353 -3 + vertex 148.124 112.282 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 148.847 111.864 -3 + vertex 147.002 112.498 -3 + vertex 148.678 111.987 -3 + endloop + endfacet + facet normal -0 -0 -1 + outer loop + vertex 148.847 111.864 -3 + vertex 149.007 111.729 -3 + vertex 147.002 112.498 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 148.678 111.987 -3 + vertex 147.002 112.498 -3 + vertex 148.5 112.098 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 148.5 112.098 -3 + vertex 147.002 112.498 -3 + vertex 148.315 112.196 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 148.315 112.196 -3 + vertex 147.002 112.498 -3 + vertex 148.124 112.282 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 149.649 110.908 -3 + vertex 147.002 112.498 -3 + vertex 149.544 111.09 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 147.726 112.411 -3 + vertex 147.002 112.498 -3 + vertex 147.521 112.454 -3 + endloop + endfacet + facet normal -0 -0 -1 + outer loop + vertex 147.726 112.411 -3 + vertex 147.927 112.353 -3 + vertex 147.002 112.498 -3 + endloop + endfacet + facet normal -0 -0 -1 + outer loop + vertex 149.819 110.526 -3 + vertex 149.884 110.327 -3 + vertex 147.002 112.498 -3 + endloop + endfacet + facet normal -0 -0 -1 + outer loop + vertex 147.314 112.484 -3 + vertex 147.521 112.454 -3 + vertex 147.002 112.498 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 147.002 112.498 -3 + vertex 149.934 110.124 -3 + vertex 149.971 109.918 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 147.002 112.498 -3 + vertex 147.105 112.498 -3 + vertex 147.314 112.484 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 150 109.5 -3 + vertex 147.002 112.498 -3 + vertex 149.993 109.709 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex 147.002 -112.498 -3 + vertex 147 112.5 -3 + vertex 147.002 112.498 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex 147.002 -112.498 -3 + vertex 147 -112.5 -3 + vertex 147 112.5 -3 + endloop + endfacet + facet normal -0 -0 -1 + outer loop + vertex 147 112.5 -3 + vertex 147 -112.5 -3 + vertex -147 112.5 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.002 -112.498 -3 + vertex -150 109.5 -3 + vertex -147.002 112.498 -3 + endloop + endfacet + facet normal -0 0 -1 + outer loop + vertex -147.002 112.498 -3 + vertex -147.314 112.484 -3 + vertex -147.105 112.498 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.002 112.498 -3 + vertex -147.521 112.454 -3 + vertex -147.314 112.484 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.002 112.498 -3 + vertex -147.726 112.411 -3 + vertex -147.521 112.454 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.002 112.498 -3 + vertex -147.927 112.353 -3 + vertex -147.726 112.411 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.002 112.498 -3 + vertex -148.124 112.282 -3 + vertex -147.927 112.353 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.002 112.498 -3 + vertex -148.315 112.196 -3 + vertex -148.124 112.282 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.002 112.498 -3 + vertex -148.5 112.098 -3 + vertex -148.315 112.196 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.002 112.498 -3 + vertex -148.678 111.987 -3 + vertex -148.5 112.098 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.002 112.498 -3 + vertex -148.847 111.864 -3 + vertex -148.678 111.987 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.002 112.498 -3 + vertex -149.007 111.729 -3 + vertex -148.847 111.864 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.002 112.498 -3 + vertex -149.158 111.584 -3 + vertex -149.007 111.729 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.002 112.498 -3 + vertex -149.298 111.428 -3 + vertex -149.158 111.584 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.002 112.498 -3 + vertex -149.427 111.263 -3 + vertex -149.298 111.428 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.002 112.498 -3 + vertex -149.544 111.09 -3 + vertex -149.427 111.263 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.002 112.498 -3 + vertex -149.649 110.908 -3 + vertex -149.544 111.09 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.002 112.498 -3 + vertex -149.741 110.72 -3 + vertex -149.649 110.908 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.002 112.498 -3 + vertex -149.819 110.526 -3 + vertex -149.741 110.72 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.002 112.498 -3 + vertex -149.884 110.327 -3 + vertex -149.819 110.526 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.002 112.498 -3 + vertex -149.934 110.124 -3 + vertex -149.884 110.327 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.002 112.498 -3 + vertex -149.971 109.918 -3 + vertex -149.934 110.124 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.002 112.498 -3 + vertex -149.993 109.709 -3 + vertex -149.971 109.918 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.002 112.498 -3 + vertex -150 109.5 -3 + vertex -149.993 109.709 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.002 -112.498 -3 + vertex -150 -109.5 -3 + vertex -150 109.5 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.521 -112.454 -3 + vertex -147.002 -112.498 -3 + vertex -147.314 -112.484 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.002 -112.498 -3 + vertex -149.971 -109.918 -3 + vertex -149.993 -109.709 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -149.884 -110.327 -3 + vertex -149.934 -110.124 -3 + vertex -147.002 -112.498 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -149.741 -110.72 -3 + vertex -147.002 -112.498 -3 + vertex -149.649 -110.908 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -149.741 -110.72 -3 + vertex -149.819 -110.526 -3 + vertex -147.002 -112.498 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.002 -112.498 -3 + vertex -149.007 -111.729 -3 + vertex -149.158 -111.584 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -149.427 -111.263 -3 + vertex -147.002 -112.498 -3 + vertex -149.298 -111.428 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -149.427 -111.263 -3 + vertex -149.544 -111.09 -3 + vertex -147.002 -112.498 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -149.298 -111.428 -3 + vertex -147.002 -112.498 -3 + vertex -149.158 -111.584 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.002 -112.498 -3 + vertex -147.927 -112.353 -3 + vertex -148.124 -112.282 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -148.847 -111.864 -3 + vertex -147.002 -112.498 -3 + vertex -148.678 -111.987 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -148.847 -111.864 -3 + vertex -149.007 -111.729 -3 + vertex -147.002 -112.498 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -148.678 -111.987 -3 + vertex -147.002 -112.498 -3 + vertex -148.5 -112.098 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -148.5 -112.098 -3 + vertex -147.002 -112.498 -3 + vertex -148.315 -112.196 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -148.315 -112.196 -3 + vertex -147.002 -112.498 -3 + vertex -148.124 -112.282 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -149.649 -110.908 -3 + vertex -147.002 -112.498 -3 + vertex -149.544 -111.09 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.726 -112.411 -3 + vertex -147.002 -112.498 -3 + vertex -147.521 -112.454 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.726 -112.411 -3 + vertex -147.927 -112.353 -3 + vertex -147.002 -112.498 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -149.819 -110.526 -3 + vertex -149.884 -110.327 -3 + vertex -147.002 -112.498 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -149.934 -110.124 -3 + vertex -149.971 -109.918 -3 + vertex -147.002 -112.498 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.105 -112.498 -3 + vertex -147.314 -112.484 -3 + vertex -147.002 -112.498 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147.002 -112.498 -3 + vertex -149.993 -109.709 -3 + vertex -150 -109.5 -3 + endloop + endfacet + facet normal 0 -0 -1 + outer loop + vertex -147 112.5 -3 + vertex -147 -112.5 -3 + vertex -147.002 112.498 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147 112.5 -3 + vertex 147 -112.5 -3 + vertex -147 -112.5 -3 + endloop + endfacet + facet normal 0 0 -1 + outer loop + vertex -147 -112.5 -3 + vertex -147.002 -112.498 -3 + vertex -147.002 112.498 -3 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 147.314 -112.484 0 + vertex 147.002 -112.498 0 + vertex 147.105 -112.498 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 147.521 -112.454 0 + vertex 147.002 -112.498 0 + vertex 147.314 -112.484 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 147.002 112.498 0 + vertex 147.002 -112.498 0 + vertex 150 -109.5 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 147.726 -112.411 0 + vertex 147.002 -112.498 0 + vertex 147.521 -112.454 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 147.927 -112.353 0 + vertex 147.002 -112.498 0 + vertex 147.726 -112.411 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 148.124 -112.282 0 + vertex 147.002 -112.498 0 + vertex 147.927 -112.353 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 148.315 -112.196 0 + vertex 147.002 -112.498 0 + vertex 148.124 -112.282 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 148.5 -112.098 0 + vertex 147.002 -112.498 0 + vertex 148.315 -112.196 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 148.678 -111.987 0 + vertex 147.002 -112.498 0 + vertex 148.5 -112.098 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 148.847 -111.864 0 + vertex 147.002 -112.498 0 + vertex 148.678 -111.987 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 149.007 -111.729 0 + vertex 147.002 -112.498 0 + vertex 148.847 -111.864 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 149.158 -111.584 0 + vertex 147.002 -112.498 0 + vertex 149.007 -111.729 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 149.298 -111.428 0 + vertex 147.002 -112.498 0 + vertex 149.158 -111.584 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 149.427 -111.263 0 + vertex 147.002 -112.498 0 + vertex 149.298 -111.428 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 149.544 -111.09 0 + vertex 147.002 -112.498 0 + vertex 149.427 -111.263 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 149.649 -110.908 0 + vertex 147.002 -112.498 0 + vertex 149.544 -111.09 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 149.741 -110.72 0 + vertex 147.002 -112.498 0 + vertex 149.649 -110.908 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 149.819 -110.526 0 + vertex 147.002 -112.498 0 + vertex 149.741 -110.72 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 149.884 -110.327 0 + vertex 147.002 -112.498 0 + vertex 149.819 -110.526 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 149.934 -110.124 0 + vertex 147.002 -112.498 0 + vertex 149.884 -110.327 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 149.971 -109.918 0 + vertex 147.002 -112.498 0 + vertex 149.934 -110.124 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 149.993 -109.709 0 + vertex 147.002 -112.498 0 + vertex 149.971 -109.918 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 150 -109.5 0 + vertex 147.002 -112.498 0 + vertex 149.993 -109.709 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 150 109.5 0 + vertex 147.002 112.498 0 + vertex 150 -109.5 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 149.993 109.709 0 + vertex 149.971 109.918 0 + vertex 147.002 112.498 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 147.002 112.498 0 + vertex 149.934 110.124 0 + vertex 149.884 110.327 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 149.649 110.908 0 + vertex 147.002 112.498 0 + vertex 149.741 110.72 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 147.002 112.498 0 + vertex 149.819 110.526 0 + vertex 149.741 110.72 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 149.158 111.584 0 + vertex 149.007 111.729 0 + vertex 147.002 112.498 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 149.298 111.428 0 + vertex 147.002 112.498 0 + vertex 149.427 111.263 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 147.002 112.498 0 + vertex 149.544 111.09 0 + vertex 149.427 111.263 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 149.158 111.584 0 + vertex 147.002 112.498 0 + vertex 149.298 111.428 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 148.124 112.282 0 + vertex 147.927 112.353 0 + vertex 147.002 112.498 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 148.678 111.987 0 + vertex 147.002 112.498 0 + vertex 148.847 111.864 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 147.002 112.498 0 + vertex 149.007 111.729 0 + vertex 148.847 111.864 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 148.5 112.098 0 + vertex 147.002 112.498 0 + vertex 148.678 111.987 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 148.315 112.196 0 + vertex 147.002 112.498 0 + vertex 148.5 112.098 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 148.124 112.282 0 + vertex 147.002 112.498 0 + vertex 148.315 112.196 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 149.544 111.09 0 + vertex 147.002 112.498 0 + vertex 149.649 110.908 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 147.521 112.454 0 + vertex 147.002 112.498 0 + vertex 147.726 112.411 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 147.002 112.498 0 + vertex 147.927 112.353 0 + vertex 147.726 112.411 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 147.002 112.498 0 + vertex 149.884 110.327 0 + vertex 149.819 110.526 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 147.002 112.498 0 + vertex 147.521 112.454 0 + vertex 147.314 112.484 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 149.971 109.918 0 + vertex 149.934 110.124 0 + vertex 147.002 112.498 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 147.314 112.484 0 + vertex 147.105 112.498 0 + vertex 147.002 112.498 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 149.993 109.709 0 + vertex 147.002 112.498 0 + vertex 150 109.5 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 147.002 112.498 0 + vertex 147 112.5 0 + vertex 147.002 -112.498 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex 147 112.5 0 + vertex 147 -112.5 0 + vertex 147.002 -112.498 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -147 112.5 0 + vertex 147 -112.5 0 + vertex 147 112.5 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -147.002 112.498 0 + vertex -150 109.5 0 + vertex -147.002 -112.498 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -147.105 112.498 0 + vertex -147.314 112.484 0 + vertex -147.002 112.498 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -147.314 112.484 0 + vertex -147.521 112.454 0 + vertex -147.002 112.498 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -147.521 112.454 0 + vertex -147.726 112.411 0 + vertex -147.002 112.498 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -147.726 112.411 0 + vertex -147.927 112.353 0 + vertex -147.002 112.498 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -147.927 112.353 0 + vertex -148.124 112.282 0 + vertex -147.002 112.498 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -148.124 112.282 0 + vertex -148.315 112.196 0 + vertex -147.002 112.498 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -148.315 112.196 0 + vertex -148.5 112.098 0 + vertex -147.002 112.498 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -148.5 112.098 0 + vertex -148.678 111.987 0 + vertex -147.002 112.498 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -148.678 111.987 0 + vertex -148.847 111.864 0 + vertex -147.002 112.498 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -148.847 111.864 0 + vertex -149.007 111.729 0 + vertex -147.002 112.498 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -149.007 111.729 0 + vertex -149.158 111.584 0 + vertex -147.002 112.498 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -149.158 111.584 0 + vertex -149.298 111.428 0 + vertex -147.002 112.498 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -149.298 111.428 0 + vertex -149.427 111.263 0 + vertex -147.002 112.498 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -149.427 111.263 0 + vertex -149.544 111.09 0 + vertex -147.002 112.498 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -149.544 111.09 0 + vertex -149.649 110.908 0 + vertex -147.002 112.498 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -149.649 110.908 0 + vertex -149.741 110.72 0 + vertex -147.002 112.498 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -149.741 110.72 0 + vertex -149.819 110.526 0 + vertex -147.002 112.498 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -149.819 110.526 0 + vertex -149.884 110.327 0 + vertex -147.002 112.498 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -149.884 110.327 0 + vertex -149.934 110.124 0 + vertex -147.002 112.498 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -149.934 110.124 0 + vertex -149.971 109.918 0 + vertex -147.002 112.498 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -149.971 109.918 0 + vertex -149.993 109.709 0 + vertex -147.002 112.498 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -149.993 109.709 0 + vertex -150 109.5 0 + vertex -147.002 112.498 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -150 109.5 0 + vertex -150 -109.5 0 + vertex -147.002 -112.498 0 + endloop + endfacet + facet normal -0 -0 1 + outer loop + vertex -147.314 -112.484 0 + vertex -147.002 -112.498 0 + vertex -147.521 -112.454 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -149.993 -109.709 0 + vertex -149.971 -109.918 0 + vertex -147.002 -112.498 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -147.002 -112.498 0 + vertex -149.934 -110.124 0 + vertex -149.884 -110.327 0 + endloop + endfacet + facet normal -0 -0 1 + outer loop + vertex -149.649 -110.908 0 + vertex -147.002 -112.498 0 + vertex -149.741 -110.72 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -147.002 -112.498 0 + vertex -149.819 -110.526 0 + vertex -149.741 -110.72 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -149.158 -111.584 0 + vertex -149.007 -111.729 0 + vertex -147.002 -112.498 0 + endloop + endfacet + facet normal -0 -0 1 + outer loop + vertex -149.298 -111.428 0 + vertex -147.002 -112.498 0 + vertex -149.427 -111.263 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -147.002 -112.498 0 + vertex -149.544 -111.09 0 + vertex -149.427 -111.263 0 + endloop + endfacet + facet normal -0 -0 1 + outer loop + vertex -149.158 -111.584 0 + vertex -147.002 -112.498 0 + vertex -149.298 -111.428 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -148.124 -112.282 0 + vertex -147.927 -112.353 0 + vertex -147.002 -112.498 0 + endloop + endfacet + facet normal -0 -0 1 + outer loop + vertex -148.678 -111.987 0 + vertex -147.002 -112.498 0 + vertex -148.847 -111.864 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -147.002 -112.498 0 + vertex -149.007 -111.729 0 + vertex -148.847 -111.864 0 + endloop + endfacet + facet normal -0 -0 1 + outer loop + vertex -148.5 -112.098 0 + vertex -147.002 -112.498 0 + vertex -148.678 -111.987 0 + endloop + endfacet + facet normal -0 -0 1 + outer loop + vertex -148.315 -112.196 0 + vertex -147.002 -112.498 0 + vertex -148.5 -112.098 0 + endloop + endfacet + facet normal -0 -0 1 + outer loop + vertex -148.124 -112.282 0 + vertex -147.002 -112.498 0 + vertex -148.315 -112.196 0 + endloop + endfacet + facet normal -0 -0 1 + outer loop + vertex -149.544 -111.09 0 + vertex -147.002 -112.498 0 + vertex -149.649 -110.908 0 + endloop + endfacet + facet normal -0 -0 1 + outer loop + vertex -147.521 -112.454 0 + vertex -147.002 -112.498 0 + vertex -147.726 -112.411 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -147.002 -112.498 0 + vertex -147.927 -112.353 0 + vertex -147.726 -112.411 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -147.002 -112.498 0 + vertex -149.884 -110.327 0 + vertex -149.819 -110.526 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -147.002 -112.498 0 + vertex -149.971 -109.918 0 + vertex -149.934 -110.124 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -147.002 -112.498 0 + vertex -147.314 -112.484 0 + vertex -147.105 -112.498 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -150 -109.5 0 + vertex -149.993 -109.709 0 + vertex -147.002 -112.498 0 + endloop + endfacet + facet normal -0 0 1 + outer loop + vertex -147.002 112.498 0 + vertex -147 -112.5 0 + vertex -147 112.5 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -147 -112.5 0 + vertex 147 -112.5 0 + vertex -147 112.5 0 + endloop + endfacet + facet normal 0 0 1 + outer loop + vertex -147.002 112.498 0 + vertex -147.002 -112.498 0 + vertex -147 -112.5 0 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex 147.105 -112.498 -3 + vertex 147.002 -112.498 0 + vertex 147.002 -112.498 -3 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex 147.105 -112.498 -3 + vertex 147.105 -112.498 0 + vertex 147.002 -112.498 0 + endloop + endfacet + facet normal 0.0668359 -0.997764 0 + outer loop + vertex 147.314 -112.484 -3 + vertex 147.105 -112.498 0 + vertex 147.105 -112.498 -3 + endloop + endfacet + facet normal 0.0668359 -0.997764 0 + outer loop + vertex 147.314 -112.484 -3 + vertex 147.314 -112.484 0 + vertex 147.105 -112.498 0 + endloop + endfacet + facet normal 0.143429 -0.989661 0 + outer loop + vertex 147.521 -112.454 -3 + vertex 147.314 -112.484 0 + vertex 147.314 -112.484 -3 + endloop + endfacet + facet normal 0.143429 -0.989661 0 + outer loop + vertex 147.521 -112.454 -3 + vertex 147.521 -112.454 0 + vertex 147.314 -112.484 0 + endloop + endfacet + facet normal 0.205289 -0.978701 0 + outer loop + vertex 147.726 -112.411 -3 + vertex 147.521 -112.454 0 + vertex 147.521 -112.454 -3 + endloop + endfacet + facet normal 0.205289 -0.978701 0 + outer loop + vertex 147.726 -112.411 -3 + vertex 147.726 -112.411 0 + vertex 147.521 -112.454 0 + endloop + endfacet + facet normal 0.277246 -0.960799 0 + outer loop + vertex 147.927 -112.353 -3 + vertex 147.726 -112.411 0 + vertex 147.726 -112.411 -3 + endloop + endfacet + facet normal 0.277246 -0.960799 0 + outer loop + vertex 147.927 -112.353 -3 + vertex 147.927 -112.353 0 + vertex 147.726 -112.411 0 + endloop + endfacet + facet normal 0.339058 -0.940766 0 + outer loop + vertex 148.124 -112.282 -3 + vertex 147.927 -112.353 0 + vertex 147.927 -112.353 -3 + endloop + endfacet + facet normal 0.339058 -0.940766 0 + outer loop + vertex 148.124 -112.282 -3 + vertex 148.124 -112.282 0 + vertex 147.927 -112.353 0 + endloop + endfacet + facet normal 0.410563 -0.911832 0 + outer loop + vertex 148.315 -112.196 -3 + vertex 148.124 -112.282 0 + vertex 148.124 -112.282 -3 + endloop + endfacet + facet normal 0.410563 -0.911832 0 + outer loop + vertex 148.315 -112.196 -3 + vertex 148.315 -112.196 0 + vertex 148.124 -112.282 0 + endloop + endfacet + facet normal 0.468107 -0.883672 0 + outer loop + vertex 148.5 -112.098 -3 + vertex 148.315 -112.196 0 + vertex 148.315 -112.196 -3 + endloop + endfacet + facet normal 0.468107 -0.883672 0 + outer loop + vertex 148.5 -112.098 -3 + vertex 148.5 -112.098 0 + vertex 148.315 -112.196 0 + endloop + endfacet + facet normal 0.529142 -0.848533 0 + outer loop + vertex 148.678 -111.987 -3 + vertex 148.5 -112.098 0 + vertex 148.5 -112.098 -3 + endloop + endfacet + facet normal 0.529142 -0.848533 0 + outer loop + vertex 148.678 -111.987 -3 + vertex 148.678 -111.987 0 + vertex 148.5 -112.098 0 + endloop + endfacet + facet normal 0.588456 -0.808529 0 + outer loop + vertex 148.847 -111.864 -3 + vertex 148.678 -111.987 0 + vertex 148.678 -111.987 -3 + endloop + endfacet + facet normal 0.588456 -0.808529 0 + outer loop + vertex 148.847 -111.864 -3 + vertex 148.847 -111.864 0 + vertex 148.678 -111.987 0 + endloop + endfacet + facet normal 0.644871 -0.764291 0 + outer loop + vertex 149.007 -111.729 -3 + vertex 148.847 -111.864 0 + vertex 148.847 -111.864 -3 + endloop + endfacet + facet normal 0.644871 -0.764291 0 + outer loop + vertex 149.007 -111.729 -3 + vertex 149.007 -111.729 0 + vertex 148.847 -111.864 0 + endloop + endfacet + facet normal 0.692631 -0.721292 0 + outer loop + vertex 149.158 -111.584 -3 + vertex 149.007 -111.729 0 + vertex 149.007 -111.729 -3 + endloop + endfacet + facet normal 0.692631 -0.721292 0 + outer loop + vertex 149.158 -111.584 -3 + vertex 149.158 -111.584 0 + vertex 149.007 -111.729 0 + endloop + endfacet + facet normal 0.744242 -0.66791 0 + outer loop + vertex 149.298 -111.428 -3 + vertex 149.158 -111.584 0 + vertex 149.158 -111.584 -3 + endloop + endfacet + facet normal 0.744242 -0.66791 0 + outer loop + vertex 149.298 -111.428 -3 + vertex 149.298 -111.428 0 + vertex 149.158 -111.584 0 + endloop + endfacet + facet normal 0.787807 -0.615922 0 + outer loop + vertex 149.427 -111.263 -3 + vertex 149.298 -111.428 0 + vertex 149.298 -111.428 -3 + endloop + endfacet + facet normal 0.787807 -0.615922 0 + outer loop + vertex 149.427 -111.263 -3 + vertex 149.427 -111.263 0 + vertex 149.298 -111.428 0 + endloop + endfacet + facet normal 0.828349 -0.560213 0 + outer loop + vertex 149.544 -111.09 -3 + vertex 149.427 -111.263 0 + vertex 149.427 -111.263 -3 + endloop + endfacet + facet normal 0.828349 -0.560213 0 + outer loop + vertex 149.544 -111.09 -3 + vertex 149.544 -111.09 0 + vertex 149.427 -111.263 0 + endloop + endfacet + facet normal 0.866186 -0.499722 0 + outer loop + vertex 149.649 -110.908 -3 + vertex 149.544 -111.09 0 + vertex 149.544 -111.09 -3 + endloop + endfacet + facet normal 0.866186 -0.499722 0 + outer loop + vertex 149.649 -110.908 -3 + vertex 149.649 -110.908 0 + vertex 149.544 -111.09 0 + endloop + endfacet + facet normal 0.898217 -0.439553 0 + outer loop + vertex 149.741 -110.72 -3 + vertex 149.649 -110.908 0 + vertex 149.649 -110.908 -3 + endloop + endfacet + facet normal 0.898217 -0.439553 0 + outer loop + vertex 149.741 -110.72 -3 + vertex 149.741 -110.72 0 + vertex 149.649 -110.908 0 + endloop + endfacet + facet normal 0.927816 -0.373039 0 + outer loop + vertex 149.819 -110.526 -3 + vertex 149.741 -110.72 0 + vertex 149.741 -110.72 -3 + endloop + endfacet + facet normal 0.927816 -0.373039 0 + outer loop + vertex 149.819 -110.526 -3 + vertex 149.819 -110.526 0 + vertex 149.741 -110.72 0 + endloop + endfacet + facet normal 0.950577 -0.31049 0 + outer loop + vertex 149.884 -110.327 -3 + vertex 149.819 -110.526 0 + vertex 149.819 -110.526 -3 + endloop + endfacet + facet normal 0.950577 -0.31049 0 + outer loop + vertex 149.884 -110.327 -3 + vertex 149.884 -110.327 0 + vertex 149.819 -110.526 0 + endloop + endfacet + facet normal 0.970981 -0.239158 0 + outer loop + vertex 149.934 -110.124 -3 + vertex 149.884 -110.327 0 + vertex 149.884 -110.327 -3 + endloop + endfacet + facet normal 0.970981 -0.239158 0 + outer loop + vertex 149.934 -110.124 -3 + vertex 149.934 -110.124 0 + vertex 149.884 -110.327 0 + endloop + endfacet + facet normal 0.98425 -0.176783 0 + outer loop + vertex 149.971 -109.918 -3 + vertex 149.934 -110.124 0 + vertex 149.934 -110.124 -3 + endloop + endfacet + facet normal 0.98425 -0.176783 0 + outer loop + vertex 149.971 -109.918 -3 + vertex 149.971 -109.918 0 + vertex 149.934 -110.124 0 + endloop + endfacet + facet normal 0.994505 -0.104685 0 + outer loop + vertex 149.993 -109.709 -3 + vertex 149.971 -109.918 0 + vertex 149.971 -109.918 -3 + endloop + endfacet + facet normal 0.994505 -0.104685 0 + outer loop + vertex 149.993 -109.709 -3 + vertex 149.993 -109.709 0 + vertex 149.971 -109.918 0 + endloop + endfacet + facet normal 0.99944 -0.0334741 0 + outer loop + vertex 150 -109.5 -3 + vertex 149.993 -109.709 0 + vertex 149.993 -109.709 -3 + endloop + endfacet + facet normal 0.99944 -0.0334741 0 + outer loop + vertex 150 -109.5 -3 + vertex 150 -109.5 0 + vertex 149.993 -109.709 0 + endloop + endfacet + facet normal 1 0 0 + outer loop + vertex 150 109.5 -3 + vertex 150 -109.5 0 + vertex 150 -109.5 -3 + endloop + endfacet + facet normal 1 0 -0 + outer loop + vertex 150 109.5 -3 + vertex 150 109.5 0 + vertex 150 -109.5 0 + endloop + endfacet + facet normal 0.99944 0.0334741 0 + outer loop + vertex 149.993 109.709 -3 + vertex 150 109.5 0 + vertex 150 109.5 -3 + endloop + endfacet + facet normal 0.99944 0.0334741 -0 + outer loop + vertex 149.993 109.709 -3 + vertex 149.993 109.709 0 + vertex 150 109.5 0 + endloop + endfacet + facet normal 0.994505 0.104685 0 + outer loop + vertex 149.971 109.918 -3 + vertex 149.993 109.709 0 + vertex 149.993 109.709 -3 + endloop + endfacet + facet normal 0.994505 0.104685 -0 + outer loop + vertex 149.971 109.918 -3 + vertex 149.971 109.918 0 + vertex 149.993 109.709 0 + endloop + endfacet + facet normal 0.98425 0.176783 0 + outer loop + vertex 149.934 110.124 -3 + vertex 149.971 109.918 0 + vertex 149.971 109.918 -3 + endloop + endfacet + facet normal 0.98425 0.176783 -0 + outer loop + vertex 149.934 110.124 -3 + vertex 149.934 110.124 0 + vertex 149.971 109.918 0 + endloop + endfacet + facet normal 0.970981 0.239158 0 + outer loop + vertex 149.884 110.327 -3 + vertex 149.934 110.124 0 + vertex 149.934 110.124 -3 + endloop + endfacet + facet normal 0.970981 0.239158 -0 + outer loop + vertex 149.884 110.327 -3 + vertex 149.884 110.327 0 + vertex 149.934 110.124 0 + endloop + endfacet + facet normal 0.950577 0.31049 0 + outer loop + vertex 149.819 110.526 -3 + vertex 149.884 110.327 0 + vertex 149.884 110.327 -3 + endloop + endfacet + facet normal 0.950577 0.31049 -0 + outer loop + vertex 149.819 110.526 -3 + vertex 149.819 110.526 0 + vertex 149.884 110.327 0 + endloop + endfacet + facet normal 0.927816 0.373039 0 + outer loop + vertex 149.741 110.72 -3 + vertex 149.819 110.526 0 + vertex 149.819 110.526 -3 + endloop + endfacet + facet normal 0.927816 0.373039 -0 + outer loop + vertex 149.741 110.72 -3 + vertex 149.741 110.72 0 + vertex 149.819 110.526 0 + endloop + endfacet + facet normal 0.898217 0.439553 0 + outer loop + vertex 149.649 110.908 -3 + vertex 149.741 110.72 0 + vertex 149.741 110.72 -3 + endloop + endfacet + facet normal 0.898217 0.439553 -0 + outer loop + vertex 149.649 110.908 -3 + vertex 149.649 110.908 0 + vertex 149.741 110.72 0 + endloop + endfacet + facet normal 0.866186 0.499722 0 + outer loop + vertex 149.544 111.09 -3 + vertex 149.649 110.908 0 + vertex 149.649 110.908 -3 + endloop + endfacet + facet normal 0.866186 0.499722 -0 + outer loop + vertex 149.544 111.09 -3 + vertex 149.544 111.09 0 + vertex 149.649 110.908 0 + endloop + endfacet + facet normal 0.828349 0.560213 0 + outer loop + vertex 149.427 111.263 -3 + vertex 149.544 111.09 0 + vertex 149.544 111.09 -3 + endloop + endfacet + facet normal 0.828349 0.560213 -0 + outer loop + vertex 149.427 111.263 -3 + vertex 149.427 111.263 0 + vertex 149.544 111.09 0 + endloop + endfacet + facet normal 0.787807 0.615922 0 + outer loop + vertex 149.298 111.428 -3 + vertex 149.427 111.263 0 + vertex 149.427 111.263 -3 + endloop + endfacet + facet normal 0.787807 0.615922 -0 + outer loop + vertex 149.298 111.428 -3 + vertex 149.298 111.428 0 + vertex 149.427 111.263 0 + endloop + endfacet + facet normal 0.744242 0.66791 0 + outer loop + vertex 149.158 111.584 -3 + vertex 149.298 111.428 0 + vertex 149.298 111.428 -3 + endloop + endfacet + facet normal 0.744242 0.66791 -0 + outer loop + vertex 149.158 111.584 -3 + vertex 149.158 111.584 0 + vertex 149.298 111.428 0 + endloop + endfacet + facet normal 0.692631 0.721292 0 + outer loop + vertex 149.007 111.729 -3 + vertex 149.158 111.584 0 + vertex 149.158 111.584 -3 + endloop + endfacet + facet normal 0.692631 0.721292 -0 + outer loop + vertex 149.007 111.729 -3 + vertex 149.007 111.729 0 + vertex 149.158 111.584 0 + endloop + endfacet + facet normal 0.644871 0.764291 0 + outer loop + vertex 148.847 111.864 -3 + vertex 149.007 111.729 0 + vertex 149.007 111.729 -3 + endloop + endfacet + facet normal 0.644871 0.764291 -0 + outer loop + vertex 148.847 111.864 -3 + vertex 148.847 111.864 0 + vertex 149.007 111.729 0 + endloop + endfacet + facet normal 0.588456 0.808529 0 + outer loop + vertex 148.678 111.987 -3 + vertex 148.847 111.864 0 + vertex 148.847 111.864 -3 + endloop + endfacet + facet normal 0.588456 0.808529 -0 + outer loop + vertex 148.678 111.987 -3 + vertex 148.678 111.987 0 + vertex 148.847 111.864 0 + endloop + endfacet + facet normal 0.529142 0.848533 0 + outer loop + vertex 148.5 112.098 -3 + vertex 148.678 111.987 0 + vertex 148.678 111.987 -3 + endloop + endfacet + facet normal 0.529142 0.848533 -0 + outer loop + vertex 148.5 112.098 -3 + vertex 148.5 112.098 0 + vertex 148.678 111.987 0 + endloop + endfacet + facet normal 0.468107 0.883672 0 + outer loop + vertex 148.315 112.196 -3 + vertex 148.5 112.098 0 + vertex 148.5 112.098 -3 + endloop + endfacet + facet normal 0.468107 0.883672 -0 + outer loop + vertex 148.315 112.196 -3 + vertex 148.315 112.196 0 + vertex 148.5 112.098 0 + endloop + endfacet + facet normal 0.410563 0.911832 0 + outer loop + vertex 148.124 112.282 -3 + vertex 148.315 112.196 0 + vertex 148.315 112.196 -3 + endloop + endfacet + facet normal 0.410563 0.911832 -0 + outer loop + vertex 148.124 112.282 -3 + vertex 148.124 112.282 0 + vertex 148.315 112.196 0 + endloop + endfacet + facet normal 0.339058 0.940766 0 + outer loop + vertex 147.927 112.353 -3 + vertex 148.124 112.282 0 + vertex 148.124 112.282 -3 + endloop + endfacet + facet normal 0.339058 0.940766 -0 + outer loop + vertex 147.927 112.353 -3 + vertex 147.927 112.353 0 + vertex 148.124 112.282 0 + endloop + endfacet + facet normal 0.277246 0.960799 0 + outer loop + vertex 147.726 112.411 -3 + vertex 147.927 112.353 0 + vertex 147.927 112.353 -3 + endloop + endfacet + facet normal 0.277246 0.960799 -0 + outer loop + vertex 147.726 112.411 -3 + vertex 147.726 112.411 0 + vertex 147.927 112.353 0 + endloop + endfacet + facet normal 0.205289 0.978701 0 + outer loop + vertex 147.521 112.454 -3 + vertex 147.726 112.411 0 + vertex 147.726 112.411 -3 + endloop + endfacet + facet normal 0.205289 0.978701 -0 + outer loop + vertex 147.521 112.454 -3 + vertex 147.521 112.454 0 + vertex 147.726 112.411 0 + endloop + endfacet + facet normal 0.143429 0.989661 0 + outer loop + vertex 147.314 112.484 -3 + vertex 147.521 112.454 0 + vertex 147.521 112.454 -3 + endloop + endfacet + facet normal 0.143429 0.989661 -0 + outer loop + vertex 147.314 112.484 -3 + vertex 147.314 112.484 0 + vertex 147.521 112.454 0 + endloop + endfacet + facet normal 0.0668359 0.997764 0 + outer loop + vertex 147.105 112.498 -3 + vertex 147.314 112.484 0 + vertex 147.314 112.484 -3 + endloop + endfacet + facet normal 0.0668359 0.997764 -0 + outer loop + vertex 147.105 112.498 -3 + vertex 147.105 112.498 0 + vertex 147.314 112.484 0 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex 147.002 112.498 -3 + vertex 147.105 112.498 0 + vertex 147.105 112.498 -3 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex 147.002 112.498 -3 + vertex 147.002 112.498 0 + vertex 147.105 112.498 0 + endloop + endfacet + facet normal 0.707107 0.707107 0 + outer loop + vertex 147 112.5 -3 + vertex 147.002 112.498 0 + vertex 147.002 112.498 -3 + endloop + endfacet + facet normal 0.707107 0.707107 -0 + outer loop + vertex 147 112.5 -3 + vertex 147 112.5 0 + vertex 147.002 112.498 0 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex -147 112.5 -3 + vertex 147 112.5 0 + vertex 147 112.5 -3 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex -147 112.5 -3 + vertex -147 112.5 0 + vertex 147 112.5 0 + endloop + endfacet + facet normal -0.707107 0.707107 0 + outer loop + vertex -147.002 112.498 -3 + vertex -147 112.5 0 + vertex -147 112.5 -3 + endloop + endfacet + facet normal -0.707107 0.707107 0 + outer loop + vertex -147.002 112.498 -3 + vertex -147.002 112.498 0 + vertex -147 112.5 0 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex -147.105 112.498 -3 + vertex -147.002 112.498 0 + vertex -147.002 112.498 -3 + endloop + endfacet + facet normal 0 1 0 + outer loop + vertex -147.105 112.498 -3 + vertex -147.105 112.498 0 + vertex -147.002 112.498 0 + endloop + endfacet + facet normal -0.0668359 0.997764 0 + outer loop + vertex -147.314 112.484 -3 + vertex -147.105 112.498 0 + vertex -147.105 112.498 -3 + endloop + endfacet + facet normal -0.0668359 0.997764 0 + outer loop + vertex -147.314 112.484 -3 + vertex -147.314 112.484 0 + vertex -147.105 112.498 0 + endloop + endfacet + facet normal -0.143429 0.989661 0 + outer loop + vertex -147.521 112.454 -3 + vertex -147.314 112.484 0 + vertex -147.314 112.484 -3 + endloop + endfacet + facet normal -0.143429 0.989661 0 + outer loop + vertex -147.521 112.454 -3 + vertex -147.521 112.454 0 + vertex -147.314 112.484 0 + endloop + endfacet + facet normal -0.205289 0.978701 0 + outer loop + vertex -147.726 112.411 -3 + vertex -147.521 112.454 0 + vertex -147.521 112.454 -3 + endloop + endfacet + facet normal -0.205289 0.978701 0 + outer loop + vertex -147.726 112.411 -3 + vertex -147.726 112.411 0 + vertex -147.521 112.454 0 + endloop + endfacet + facet normal -0.277246 0.960799 0 + outer loop + vertex -147.927 112.353 -3 + vertex -147.726 112.411 0 + vertex -147.726 112.411 -3 + endloop + endfacet + facet normal -0.277246 0.960799 0 + outer loop + vertex -147.927 112.353 -3 + vertex -147.927 112.353 0 + vertex -147.726 112.411 0 + endloop + endfacet + facet normal -0.339058 0.940766 0 + outer loop + vertex -148.124 112.282 -3 + vertex -147.927 112.353 0 + vertex -147.927 112.353 -3 + endloop + endfacet + facet normal -0.339058 0.940766 0 + outer loop + vertex -148.124 112.282 -3 + vertex -148.124 112.282 0 + vertex -147.927 112.353 0 + endloop + endfacet + facet normal -0.410563 0.911832 0 + outer loop + vertex -148.315 112.196 -3 + vertex -148.124 112.282 0 + vertex -148.124 112.282 -3 + endloop + endfacet + facet normal -0.410563 0.911832 0 + outer loop + vertex -148.315 112.196 -3 + vertex -148.315 112.196 0 + vertex -148.124 112.282 0 + endloop + endfacet + facet normal -0.468107 0.883672 0 + outer loop + vertex -148.5 112.098 -3 + vertex -148.315 112.196 0 + vertex -148.315 112.196 -3 + endloop + endfacet + facet normal -0.468107 0.883672 0 + outer loop + vertex -148.5 112.098 -3 + vertex -148.5 112.098 0 + vertex -148.315 112.196 0 + endloop + endfacet + facet normal -0.529142 0.848533 0 + outer loop + vertex -148.678 111.987 -3 + vertex -148.5 112.098 0 + vertex -148.5 112.098 -3 + endloop + endfacet + facet normal -0.529142 0.848533 0 + outer loop + vertex -148.678 111.987 -3 + vertex -148.678 111.987 0 + vertex -148.5 112.098 0 + endloop + endfacet + facet normal -0.588456 0.808529 0 + outer loop + vertex -148.847 111.864 -3 + vertex -148.678 111.987 0 + vertex -148.678 111.987 -3 + endloop + endfacet + facet normal -0.588456 0.808529 0 + outer loop + vertex -148.847 111.864 -3 + vertex -148.847 111.864 0 + vertex -148.678 111.987 0 + endloop + endfacet + facet normal -0.644871 0.764291 0 + outer loop + vertex -149.007 111.729 -3 + vertex -148.847 111.864 0 + vertex -148.847 111.864 -3 + endloop + endfacet + facet normal -0.644871 0.764291 0 + outer loop + vertex -149.007 111.729 -3 + vertex -149.007 111.729 0 + vertex -148.847 111.864 0 + endloop + endfacet + facet normal -0.692631 0.721292 0 + outer loop + vertex -149.158 111.584 -3 + vertex -149.007 111.729 0 + vertex -149.007 111.729 -3 + endloop + endfacet + facet normal -0.692631 0.721292 0 + outer loop + vertex -149.158 111.584 -3 + vertex -149.158 111.584 0 + vertex -149.007 111.729 0 + endloop + endfacet + facet normal -0.744242 0.66791 0 + outer loop + vertex -149.298 111.428 -3 + vertex -149.158 111.584 0 + vertex -149.158 111.584 -3 + endloop + endfacet + facet normal -0.744242 0.66791 0 + outer loop + vertex -149.298 111.428 -3 + vertex -149.298 111.428 0 + vertex -149.158 111.584 0 + endloop + endfacet + facet normal -0.787807 0.615922 0 + outer loop + vertex -149.427 111.263 -3 + vertex -149.298 111.428 0 + vertex -149.298 111.428 -3 + endloop + endfacet + facet normal -0.787807 0.615922 0 + outer loop + vertex -149.427 111.263 -3 + vertex -149.427 111.263 0 + vertex -149.298 111.428 0 + endloop + endfacet + facet normal -0.828349 0.560213 0 + outer loop + vertex -149.544 111.09 -3 + vertex -149.427 111.263 0 + vertex -149.427 111.263 -3 + endloop + endfacet + facet normal -0.828349 0.560213 0 + outer loop + vertex -149.544 111.09 -3 + vertex -149.544 111.09 0 + vertex -149.427 111.263 0 + endloop + endfacet + facet normal -0.866186 0.499722 0 + outer loop + vertex -149.649 110.908 -3 + vertex -149.544 111.09 0 + vertex -149.544 111.09 -3 + endloop + endfacet + facet normal -0.866186 0.499722 0 + outer loop + vertex -149.649 110.908 -3 + vertex -149.649 110.908 0 + vertex -149.544 111.09 0 + endloop + endfacet + facet normal -0.898217 0.439553 0 + outer loop + vertex -149.741 110.72 -3 + vertex -149.649 110.908 0 + vertex -149.649 110.908 -3 + endloop + endfacet + facet normal -0.898217 0.439553 0 + outer loop + vertex -149.741 110.72 -3 + vertex -149.741 110.72 0 + vertex -149.649 110.908 0 + endloop + endfacet + facet normal -0.927816 0.373039 0 + outer loop + vertex -149.819 110.526 -3 + vertex -149.741 110.72 0 + vertex -149.741 110.72 -3 + endloop + endfacet + facet normal -0.927816 0.373039 0 + outer loop + vertex -149.819 110.526 -3 + vertex -149.819 110.526 0 + vertex -149.741 110.72 0 + endloop + endfacet + facet normal -0.950577 0.31049 0 + outer loop + vertex -149.884 110.327 -3 + vertex -149.819 110.526 0 + vertex -149.819 110.526 -3 + endloop + endfacet + facet normal -0.950577 0.31049 0 + outer loop + vertex -149.884 110.327 -3 + vertex -149.884 110.327 0 + vertex -149.819 110.526 0 + endloop + endfacet + facet normal -0.970981 0.239158 0 + outer loop + vertex -149.934 110.124 -3 + vertex -149.884 110.327 0 + vertex -149.884 110.327 -3 + endloop + endfacet + facet normal -0.970981 0.239158 0 + outer loop + vertex -149.934 110.124 -3 + vertex -149.934 110.124 0 + vertex -149.884 110.327 0 + endloop + endfacet + facet normal -0.98425 0.176783 0 + outer loop + vertex -149.971 109.918 -3 + vertex -149.934 110.124 0 + vertex -149.934 110.124 -3 + endloop + endfacet + facet normal -0.98425 0.176783 0 + outer loop + vertex -149.971 109.918 -3 + vertex -149.971 109.918 0 + vertex -149.934 110.124 0 + endloop + endfacet + facet normal -0.994505 0.104685 0 + outer loop + vertex -149.993 109.709 -3 + vertex -149.971 109.918 0 + vertex -149.971 109.918 -3 + endloop + endfacet + facet normal -0.994505 0.104685 0 + outer loop + vertex -149.993 109.709 -3 + vertex -149.993 109.709 0 + vertex -149.971 109.918 0 + endloop + endfacet + facet normal -0.99944 0.0334741 0 + outer loop + vertex -150 109.5 -3 + vertex -149.993 109.709 0 + vertex -149.993 109.709 -3 + endloop + endfacet + facet normal -0.99944 0.0334741 0 + outer loop + vertex -150 109.5 -3 + vertex -150 109.5 0 + vertex -149.993 109.709 0 + endloop + endfacet + facet normal -1 0 0 + outer loop + vertex -150 -109.5 -3 + vertex -150 109.5 0 + vertex -150 109.5 -3 + endloop + endfacet + facet normal -1 0 0 + outer loop + vertex -150 -109.5 -3 + vertex -150 -109.5 0 + vertex -150 109.5 0 + endloop + endfacet + facet normal -0.99944 -0.0334741 0 + outer loop + vertex -149.993 -109.709 -3 + vertex -150 -109.5 0 + vertex -150 -109.5 -3 + endloop + endfacet + facet normal -0.99944 -0.0334741 0 + outer loop + vertex -149.993 -109.709 -3 + vertex -149.993 -109.709 0 + vertex -150 -109.5 0 + endloop + endfacet + facet normal -0.994505 -0.104685 0 + outer loop + vertex -149.971 -109.918 -3 + vertex -149.993 -109.709 0 + vertex -149.993 -109.709 -3 + endloop + endfacet + facet normal -0.994505 -0.104685 0 + outer loop + vertex -149.971 -109.918 -3 + vertex -149.971 -109.918 0 + vertex -149.993 -109.709 0 + endloop + endfacet + facet normal -0.98425 -0.176783 0 + outer loop + vertex -149.934 -110.124 -3 + vertex -149.971 -109.918 0 + vertex -149.971 -109.918 -3 + endloop + endfacet + facet normal -0.98425 -0.176783 0 + outer loop + vertex -149.934 -110.124 -3 + vertex -149.934 -110.124 0 + vertex -149.971 -109.918 0 + endloop + endfacet + facet normal -0.970981 -0.239158 0 + outer loop + vertex -149.884 -110.327 -3 + vertex -149.934 -110.124 0 + vertex -149.934 -110.124 -3 + endloop + endfacet + facet normal -0.970981 -0.239158 0 + outer loop + vertex -149.884 -110.327 -3 + vertex -149.884 -110.327 0 + vertex -149.934 -110.124 0 + endloop + endfacet + facet normal -0.950577 -0.31049 0 + outer loop + vertex -149.819 -110.526 -3 + vertex -149.884 -110.327 0 + vertex -149.884 -110.327 -3 + endloop + endfacet + facet normal -0.950577 -0.31049 0 + outer loop + vertex -149.819 -110.526 -3 + vertex -149.819 -110.526 0 + vertex -149.884 -110.327 0 + endloop + endfacet + facet normal -0.927816 -0.373039 0 + outer loop + vertex -149.741 -110.72 -3 + vertex -149.819 -110.526 0 + vertex -149.819 -110.526 -3 + endloop + endfacet + facet normal -0.927816 -0.373039 0 + outer loop + vertex -149.741 -110.72 -3 + vertex -149.741 -110.72 0 + vertex -149.819 -110.526 0 + endloop + endfacet + facet normal -0.898217 -0.439553 0 + outer loop + vertex -149.649 -110.908 -3 + vertex -149.741 -110.72 0 + vertex -149.741 -110.72 -3 + endloop + endfacet + facet normal -0.898217 -0.439553 0 + outer loop + vertex -149.649 -110.908 -3 + vertex -149.649 -110.908 0 + vertex -149.741 -110.72 0 + endloop + endfacet + facet normal -0.866186 -0.499722 0 + outer loop + vertex -149.544 -111.09 -3 + vertex -149.649 -110.908 0 + vertex -149.649 -110.908 -3 + endloop + endfacet + facet normal -0.866186 -0.499722 0 + outer loop + vertex -149.544 -111.09 -3 + vertex -149.544 -111.09 0 + vertex -149.649 -110.908 0 + endloop + endfacet + facet normal -0.828349 -0.560213 0 + outer loop + vertex -149.427 -111.263 -3 + vertex -149.544 -111.09 0 + vertex -149.544 -111.09 -3 + endloop + endfacet + facet normal -0.828349 -0.560213 0 + outer loop + vertex -149.427 -111.263 -3 + vertex -149.427 -111.263 0 + vertex -149.544 -111.09 0 + endloop + endfacet + facet normal -0.787807 -0.615922 0 + outer loop + vertex -149.298 -111.428 -3 + vertex -149.427 -111.263 0 + vertex -149.427 -111.263 -3 + endloop + endfacet + facet normal -0.787807 -0.615922 0 + outer loop + vertex -149.298 -111.428 -3 + vertex -149.298 -111.428 0 + vertex -149.427 -111.263 0 + endloop + endfacet + facet normal -0.744242 -0.66791 0 + outer loop + vertex -149.158 -111.584 -3 + vertex -149.298 -111.428 0 + vertex -149.298 -111.428 -3 + endloop + endfacet + facet normal -0.744242 -0.66791 0 + outer loop + vertex -149.158 -111.584 -3 + vertex -149.158 -111.584 0 + vertex -149.298 -111.428 0 + endloop + endfacet + facet normal -0.692631 -0.721292 0 + outer loop + vertex -149.007 -111.729 -3 + vertex -149.158 -111.584 0 + vertex -149.158 -111.584 -3 + endloop + endfacet + facet normal -0.692631 -0.721292 0 + outer loop + vertex -149.007 -111.729 -3 + vertex -149.007 -111.729 0 + vertex -149.158 -111.584 0 + endloop + endfacet + facet normal -0.644871 -0.764291 0 + outer loop + vertex -148.847 -111.864 -3 + vertex -149.007 -111.729 0 + vertex -149.007 -111.729 -3 + endloop + endfacet + facet normal -0.644871 -0.764291 0 + outer loop + vertex -148.847 -111.864 -3 + vertex -148.847 -111.864 0 + vertex -149.007 -111.729 0 + endloop + endfacet + facet normal -0.588456 -0.808529 0 + outer loop + vertex -148.678 -111.987 -3 + vertex -148.847 -111.864 0 + vertex -148.847 -111.864 -3 + endloop + endfacet + facet normal -0.588456 -0.808529 0 + outer loop + vertex -148.678 -111.987 -3 + vertex -148.678 -111.987 0 + vertex -148.847 -111.864 0 + endloop + endfacet + facet normal -0.529142 -0.848533 0 + outer loop + vertex -148.5 -112.098 -3 + vertex -148.678 -111.987 0 + vertex -148.678 -111.987 -3 + endloop + endfacet + facet normal -0.529142 -0.848533 0 + outer loop + vertex -148.5 -112.098 -3 + vertex -148.5 -112.098 0 + vertex -148.678 -111.987 0 + endloop + endfacet + facet normal -0.468107 -0.883672 0 + outer loop + vertex -148.315 -112.196 -3 + vertex -148.5 -112.098 0 + vertex -148.5 -112.098 -3 + endloop + endfacet + facet normal -0.468107 -0.883672 0 + outer loop + vertex -148.315 -112.196 -3 + vertex -148.315 -112.196 0 + vertex -148.5 -112.098 0 + endloop + endfacet + facet normal -0.410563 -0.911832 0 + outer loop + vertex -148.124 -112.282 -3 + vertex -148.315 -112.196 0 + vertex -148.315 -112.196 -3 + endloop + endfacet + facet normal -0.410563 -0.911832 0 + outer loop + vertex -148.124 -112.282 -3 + vertex -148.124 -112.282 0 + vertex -148.315 -112.196 0 + endloop + endfacet + facet normal -0.339058 -0.940766 0 + outer loop + vertex -147.927 -112.353 -3 + vertex -148.124 -112.282 0 + vertex -148.124 -112.282 -3 + endloop + endfacet + facet normal -0.339058 -0.940766 0 + outer loop + vertex -147.927 -112.353 -3 + vertex -147.927 -112.353 0 + vertex -148.124 -112.282 0 + endloop + endfacet + facet normal -0.277246 -0.960799 0 + outer loop + vertex -147.726 -112.411 -3 + vertex -147.927 -112.353 0 + vertex -147.927 -112.353 -3 + endloop + endfacet + facet normal -0.277246 -0.960799 0 + outer loop + vertex -147.726 -112.411 -3 + vertex -147.726 -112.411 0 + vertex -147.927 -112.353 0 + endloop + endfacet + facet normal -0.205289 -0.978701 0 + outer loop + vertex -147.521 -112.454 -3 + vertex -147.726 -112.411 0 + vertex -147.726 -112.411 -3 + endloop + endfacet + facet normal -0.205289 -0.978701 0 + outer loop + vertex -147.521 -112.454 -3 + vertex -147.521 -112.454 0 + vertex -147.726 -112.411 0 + endloop + endfacet + facet normal -0.143429 -0.989661 0 + outer loop + vertex -147.314 -112.484 -3 + vertex -147.521 -112.454 0 + vertex -147.521 -112.454 -3 + endloop + endfacet + facet normal -0.143429 -0.989661 0 + outer loop + vertex -147.314 -112.484 -3 + vertex -147.314 -112.484 0 + vertex -147.521 -112.454 0 + endloop + endfacet + facet normal -0.0668359 -0.997764 0 + outer loop + vertex -147.105 -112.498 -3 + vertex -147.314 -112.484 0 + vertex -147.314 -112.484 -3 + endloop + endfacet + facet normal -0.0668359 -0.997764 0 + outer loop + vertex -147.105 -112.498 -3 + vertex -147.105 -112.498 0 + vertex -147.314 -112.484 0 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex -147.002 -112.498 -3 + vertex -147.105 -112.498 0 + vertex -147.105 -112.498 -3 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex -147.002 -112.498 -3 + vertex -147.002 -112.498 0 + vertex -147.105 -112.498 0 + endloop + endfacet + facet normal -0.707107 -0.707107 0 + outer loop + vertex -147 -112.5 -3 + vertex -147.002 -112.498 0 + vertex -147.002 -112.498 -3 + endloop + endfacet + facet normal -0.707107 -0.707107 0 + outer loop + vertex -147 -112.5 -3 + vertex -147 -112.5 0 + vertex -147.002 -112.498 0 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex 147 -112.5 -3 + vertex -147 -112.5 0 + vertex -147 -112.5 -3 + endloop + endfacet + facet normal 0 -1 0 + outer loop + vertex 147 -112.5 -3 + vertex 147 -112.5 0 + vertex -147 -112.5 0 + endloop + endfacet + facet normal 0.707107 -0.707107 0 + outer loop + vertex 147.002 -112.498 -3 + vertex 147 -112.5 0 + vertex 147 -112.5 -3 + endloop + endfacet + facet normal 0.707107 -0.707107 0 + outer loop + vertex 147.002 -112.498 -3 + vertex 147.002 -112.498 0 + vertex 147 -112.5 0 + endloop + endfacet +endsolid OpenSCAD_Model From 90f58fe90a3f8ac23937ed8af34def83bc4560ff Mon Sep 17 00:00:00 2001 From: Pascal de Bruijn Date: Sun, 28 Feb 2021 17:06:24 +0100 Subject: [PATCH 021/285] creality.ini: CR-200B bed_shape and max_print_height --- resources/profiles/Creality.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index de6f103a0e..2c1b9da208 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -1017,6 +1017,8 @@ printer_notes = Don't remove the following keywords! These keywords are used in #[printer:Creality CR-200B] #inherits = *common* +#bed_shape = 5x5,195x5,195x195,5x195 +#max_print_height = 200 #printer_model = CR200B #printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR200B\nPRINTER_HAS_BOWDEN From 34202f71e5ab56c5e96fb3c9d9e6262b4cdd1231 Mon Sep 17 00:00:00 2001 From: Pascal de Bruijn Date: Sun, 28 Feb 2021 17:10:25 +0100 Subject: [PATCH 022/285] creality.ini: move Ender-3 specific extruder_clearance replace general extruder_clearance with higher values so there's a lot of clearance leeway until we get exact figures for each printer model. related: f9b4bbb016566eead32d32092939a88c10c80f1f --- resources/profiles/Creality.ini | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index 2c1b9da208..62d0d837f9 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -287,8 +287,8 @@ external_perimeters_first = 0 external_perimeter_extrusion_width = 0.45 external_perimeter_speed = 25 extra_perimeters = 0 -extruder_clearance_height = 34 -extruder_clearance_radius = 47 +extruder_clearance_height = 50 +extruder_clearance_radius = 70 extrusion_width = 0.45 fill_angle = 45 fill_density = 15% @@ -834,6 +834,8 @@ inherits = *common* renamed_from = "Creality ENDER-3" bed_shape = 3x3,228x3,228x228,3x228 max_print_height = 250 +extruder_clearance_height = 34 +extruder_clearance_radius = 47 printer_model = ENDER3 printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER3\nPRINTER_HAS_BOWDEN From 3f1299c9b7e0a8689d5a16b8d704ed413419c7fa Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 1 Mar 2021 10:03:49 +0100 Subject: [PATCH 023/285] Fix build against wxWidgets older than 3.1.1 --- src/slic3r/GUI/ConfigWizard.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 77e8472016..bd6986d3ef 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -819,7 +819,22 @@ void PageMaterials::update_lists(int sel_type, int sel_vendor, int last_selected wxArrayInt sel_printers; int sel_printers_count = list_printer->GetSelections(sel_printers); - if (sel_printers != sel_printers_prev) { + // Does our wxWidgets version support operator== for wxArrayInt ? + // https://github.com/prusa3d/PrusaSlicer/issues/5152#issuecomment-787208614 +#if wxCHECK_VERSION(3, 1, 1) + if (sel_printers != sel_printers_prev) { +#else + auto are_equal = [](const wxArrayInt& arr_first, const wxArrayInt& arr_second) { + if (arr_first.GetCount() != arr_second.GetCount()) + return false; + for (size_t i = 0; i < arr_first.GetCount(); i++) + if (arr_first[i] != arr_second[i]) + return false; + return true; + }; + if (!are_equal(sel_printers, sel_printers_prev)) { +#endif + // Refresh type list list_type->Clear(); list_type->append(_L("(All)"), &EMPTY); From 13b3e813cfe2b9bd4887ba2e0fa9938b6352aad8 Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Mon, 1 Mar 2021 10:50:41 +0100 Subject: [PATCH 024/285] CR5PRO and CR200B thumbnails --- .../profiles/Creality/CR200B_thumbnail.png | Bin 0 -> 27228 bytes .../profiles/Creality/CR5PRO_thumbnail.png | Bin 0 -> 38706 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 resources/profiles/Creality/CR200B_thumbnail.png create mode 100644 resources/profiles/Creality/CR5PRO_thumbnail.png diff --git a/resources/profiles/Creality/CR200B_thumbnail.png b/resources/profiles/Creality/CR200B_thumbnail.png new file mode 100644 index 0000000000000000000000000000000000000000..d5b964ac937c400e81e11ca0e8e3681ecf7db315 GIT binary patch literal 27228 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 UJIxV%?GT!hyqa9O%+tXC19$y2#Q*>R literal 0 HcmV?d00001 diff --git a/resources/profiles/Creality/CR5PRO_thumbnail.png b/resources/profiles/Creality/CR5PRO_thumbnail.png new file mode 100644 index 0000000000000000000000000000000000000000..df2f1b665f2f7a3d7d5e45ff3bb29a4fb674bdfe GIT binary patch literal 38706 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 literal 0 HcmV?d00001 From d7a786e8c8d82eb0be95e81094665e0fb23ce66b Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 1 Mar 2021 10:43:13 +0100 Subject: [PATCH 025/285] Bumping up wxWidgets to 3.1.4-patched on all platforms. --- deps/wxWidgets/wxWidgets.cmake | 9 +-------- src/libslic3r/Geometry.cpp | 6 ++---- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/deps/wxWidgets/wxWidgets.cmake b/deps/wxWidgets/wxWidgets.cmake index bf23698cf4..c7facc2c62 100644 --- a/deps/wxWidgets/wxWidgets.cmake +++ b/deps/wxWidgets/wxWidgets.cmake @@ -1,11 +1,4 @@ -if (APPLE) - # The new OSX 11 (Big Sur) is not compatible with wxWidgets 3.1.3. - # Let's use patched wxWidgets 3.1.4, even though it is not quite tested. - set(_wx_git_tag v3.1.4-patched) -else () - # Use the tested patched wxWidgets 3.1.3 everywhere else. - set(_wx_git_tag v3.1.3-patched) -endif () +set(_wx_git_tag v3.1.4-patched) # set(_patch_command "") set(_wx_toolkit "") diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index 45730dd9f5..aac7c77524 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -245,8 +245,7 @@ Polygon convex_hull(Points points) return hull; } -Pointf3s -convex_hull(Pointf3s points) +Pointf3s convex_hull(Pointf3s points) { assert(points.size() >= 3); // sort input points @@ -304,8 +303,7 @@ convex_hull(Pointf3s points) return hull; } -Polygon -convex_hull(const Polygons &polygons) +Polygon convex_hull(const Polygons &polygons) { Points pp; for (Polygons::const_iterator p = polygons.begin(); p != polygons.end(); ++p) { From 224f3e506c9db3d2de9e2e441ee4443de2bab964 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 1 Mar 2021 12:52:40 +0100 Subject: [PATCH 026/285] Fix of Custom G-code for color change does not process macro #5782 Added macro processing of color_change_gcode, pause_print_gcode and template_custom_gcode custom G-codes when emitting them into the output G-code. --- src/libslic3r/GCode.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 8384359610..ca714549db 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1705,7 +1705,9 @@ namespace ProcessLayer { static std::string emit_custom_gcode_per_print_z( + GCode &gcodegen, const CustomGCode::Item *custom_gcode, + unsigned int current_extruder_id, // ID of the first extruder printing this layer. unsigned int first_extruder_id, const PrintConfig &config) @@ -1746,12 +1748,14 @@ namespace ProcessLayer // && !MMU1 ) { //! FIXME_in_fw show message during print pause - gcode += config.pause_print_gcode;// pause print + DynamicConfig cfg; + cfg.set_key_value("color_change_extruder", new ConfigOptionInt(m600_extruder_before_layer)); + gcode += gcodegen.placeholder_parser_process("pause_print_gcode", config.pause_print_gcode, current_extruder_id, &cfg); gcode += "\n"; gcode += "M117 Change filament for Extruder " + std::to_string(m600_extruder_before_layer) + "\n"; } else { - gcode += config.color_change_gcode;//ColorChangeCode; + gcode += gcodegen.placeholder_parser_process("color_change_gcode", config.color_change_gcode, current_extruder_id); gcode += "\n"; } } @@ -1767,7 +1771,7 @@ namespace ProcessLayer //! FIXME_in_fw show message during print pause if (!pause_print_msg.empty()) gcode += "M117 " + pause_print_msg + "\n"; - gcode += config.pause_print_gcode; + gcode += gcodegen.placeholder_parser_process("pause_print_gcode", config.pause_print_gcode, current_extruder_id); } else { // add tag for processor @@ -1776,8 +1780,8 @@ namespace ProcessLayer #else gcode += ";" + GCodeProcessor::Custom_Code_Tag + "\n"; #endif // ENABLE_VALIDATE_CUSTOM_GCODE - if (gcode_type == CustomGCode::Template) // Template Cistom Gcode - gcode += config.template_custom_gcode; + if (gcode_type == CustomGCode::Template) // Template Custom Gcode + gcode += gcodegen.placeholder_parser_process("template_custom_gcode", config.template_custom_gcode, current_extruder_id); else // custom Gcode gcode += custom_gcode->extra; @@ -1985,7 +1989,7 @@ void GCode::process_layer( if (single_object_instance_idx == size_t(-1)) { // Normal (non-sequential) print. - gcode += ProcessLayer::emit_custom_gcode_per_print_z(layer_tools.custom_gcode, first_extruder_id, print.config()); + gcode += ProcessLayer::emit_custom_gcode_per_print_z(*this, layer_tools.custom_gcode, m_writer.extruder()->id(), first_extruder_id, print.config()); } // Extrude skirt at the print_z of the raft layers and normal object layers // not at the print_z of the interlaced support material layers. From 908c48ae6a1950eec1cf76840a06ee84e83ea155 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 1 Mar 2021 13:03:43 +0100 Subject: [PATCH 027/285] Follow-up of 86d7e1fb907d9841a1d0cf516415fea84e8b5280 -> Fixed update after switching tab after editing custom g-code in settings tabs --- src/slic3r/GUI/MainFrame.cpp | 5 ++--- src/slic3r/GUI/Tab.cpp | 36 ++++++++++++++++++++++++++++-------- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 91357ff6c4..6b0dd1ad5a 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -549,9 +549,8 @@ void MainFrame::init_tabpanel() wxWindow* panel = m_tabpanel->GetCurrentPage(); if (panel != nullptr) { Tab* tab = dynamic_cast(panel); - if (tab && (tab->type() == Preset::TYPE_FILAMENT || tab->type() == Preset::TYPE_PRINTER)) - if (!tab->validate_custom_gcodes()) - evt.Veto(); + if (tab != nullptr && !tab->validate_custom_gcodes()) + evt.Veto(); } }); #endif // ENABLE_VALIDATE_CUSTOM_GCODE diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index cf1879bb16..eeb420e3e0 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3826,15 +3826,35 @@ void TabPrinter::apply_extruder_cnt_from_cache() #if ENABLE_VALIDATE_CUSTOM_GCODE bool Tab::validate_custom_gcodes() { + if (m_type != Preset::TYPE_FILAMENT && + (m_type != Preset::TYPE_PRINTER || static_cast(this)->m_printer_technology != ptFFF)) + return true; + if (m_active_page->title() != L("Custom G-code")) + return true; + bool valid = true; - if ((m_type == Preset::TYPE_FILAMENT || - (m_type == Preset::TYPE_PRINTER && static_cast(this)->m_printer_technology == ptFFF)) && - m_active_page->title() == "Custom G-code") { - for (auto opt_group : m_active_page->m_optgroups) { - assert(opt_group->opt_map().size() == 1); - std::string key = opt_group->opt_map().begin()->first; - valid &= validate_custom_gcode(opt_group->title, boost::any_cast(opt_group->get_value(key))); - } + for (auto opt_group : m_active_page->m_optgroups) { + assert(opt_group->opt_map().size() == 1); + std::string key = opt_group->opt_map().begin()->first; + std::string value = boost::any_cast(opt_group->get_value(key)); + std::string config_value = m_config->opt_string(key); + valid &= validate_custom_gcode(opt_group->title, value); + Field* field = opt_group->get_field(key); + TextCtrl* text_ctrl = dynamic_cast(field); + if (text_ctrl != nullptr && text_ctrl->m_on_change != nullptr && !text_ctrl->m_disable_change_event) { + Slic3r::GUI::t_change callback = opt_group->m_on_change; + // temporary disable the opt_group->m_on_change callback to avoid multiple validations + opt_group->m_on_change = nullptr; + text_ctrl->m_on_change(key, value); + // restore the opt_group->m_on_change callback + opt_group->m_on_change = callback; + + update_dirty(); + on_value_change(key, value); + } + + if (!valid) + break; } return valid; } From 70fc0c232c745a74d3e8459422812e7edfc26ac3 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 1 Mar 2021 16:01:53 +0100 Subject: [PATCH 028/285] Linux specific issue: DiffViewCtrl: Fixed scaling of the columns width --- src/slic3r/GUI/UnsavedChangesDialog.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 7d2879f0a3..7c0be76a4a 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -601,7 +601,7 @@ void DiffViewCtrl::AppendBmpTextColumn(const wxString& label, unsigned model_col #ifdef SUPPORTS_MARKUP rd->EnableMarkup(true); #endif - wxDataViewColumn* column = new wxDataViewColumn(label, rd, model_column, width, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_CELL_INERT); + wxDataViewColumn* column = new wxDataViewColumn(label, rd, model_column, width * m_em_unit, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_CELL_INERT); #else wxDataViewColumn* column = new wxDataViewColumn(label, new BitmapTextRenderer(true, wxDATAVIEW_CELL_INERT), model_column, width * m_em_unit, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE); #endif //__linux__ @@ -806,7 +806,7 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_ m_action_line->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold()); m_tree = new DiffViewCtrl(this, wxSize(em * 60, em * 30)); - m_tree->AppendToggleColumn_(L"\u2714" , DiffModel::colToggle, wxLinux ? 8 : 6); + m_tree->AppendToggleColumn_(L"\u2714" , DiffModel::colToggle, wxLinux ? 9 : 6); m_tree->AppendBmpTextColumn("" , DiffModel::colIconText, 28); m_tree->AppendBmpTextColumn(_L("Old Value"), DiffModel::colOldValue, 12); m_tree->AppendBmpTextColumn(_L("New Value"), DiffModel::colNewValue, 12); From 409849d238718b75cf0c5ef264ce1df0cedc5c6e Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 1 Mar 2021 16:32:47 +0100 Subject: [PATCH 029/285] Fixed loading of the project file with filament which is not installed for the selected printers If loaded filaments are invisible/non-instaled, set them as visible --- src/libslic3r/PresetBundle.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 7d08c93592..dc52fdaff8 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -876,6 +876,17 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool this->filament_presets[i] = loaded->name; } } + + // 3.1) If loaded filaments are invisible/non-instaled, set them as visible + for (const std::string& filament : this->filament_presets) { + Preset* preset = this->filaments.find_preset(filament); + if (preset && !preset->is_visible) { + preset->is_visible = true; + if (preset->name == this->filaments.m_edited_preset.name) + this->filaments.get_selected_preset().is_visible = true; + } + } + // 4) Load the project config values (the per extruder wipe matrix etc). this->project_config.apply_only(config, s_project_options); From 5e78b0f24f17380653ea299da561b7b7b0041711 Mon Sep 17 00:00:00 2001 From: Pascal de Bruijn Date: Mon, 1 Mar 2021 18:21:27 +0100 Subject: [PATCH 030/285] creality.ini: remove ignored renamed_from from *descendingz* fixes: Nonpublic intermediate preset *descendingz* contains a "renamed_from" field, which is ignored --- resources/profiles/Creality.ini | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index 62d0d837f9..d0e2bac3bd 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -799,7 +799,6 @@ pause_print_gcode = M25 ; pause print # Intended for printers where the Z-axis lowers the print bed during printing, like the Ender-5 series [printer:*descendingz*] -renamed_from = "*invertedz*" end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+2, max_print_height)} F600{endif} ; Move print bed down\nG1 X50 Y50 F{travel_speed*60} ; move print head out of the way\n{if max_layer_z < max_print_height-10}G1 Z{z_offset+max_print_height-10} F600{endif} ; Move print bed close to the bottom\nM140 S0 ; turn off heatbed\nM104 S0 ; turn off temperature\nM107 ; turn off fan\nM84 X Y E ; disable motors # Intended for printers with dual extruders and a single hotend/nozzle, like the CR-X series From f5863cd5d89a271178cb0e07264d493be5f61119 Mon Sep 17 00:00:00 2001 From: Pascal de Bruijn Date: Mon, 1 Mar 2021 18:24:44 +0100 Subject: [PATCH 031/285] creality.ini: CR-200B also has a descending z axis --- resources/profiles/Creality.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index d0e2bac3bd..b7c0f4e96e 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -1017,7 +1017,7 @@ printer_model = CR20PRO printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR20PRO\nPRINTER_HAS_BOWDEN #[printer:Creality CR-200B] -#inherits = *common* +#inherits = *common*; *descendingz* #bed_shape = 5x5,195x5,195x195,5x195 #max_print_height = 200 #printer_model = CR200B From 586ed35e3a688500c05b1823b8b795845622c6d3 Mon Sep 17 00:00:00 2001 From: Pascal de Bruijn Date: Mon, 1 Mar 2021 18:34:18 +0100 Subject: [PATCH 032/285] creality.ini: fix CR-5 Pro bed_shape --- resources/profiles/Creality.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index b7c0f4e96e..f68d067d35 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -906,7 +906,7 @@ printer_notes = Don't remove the following keywords! These keywords are used in #[printer:Creality CR-5 Pro] #inherits = *common*; *slowabl*; *descendingz* #retract_length = 3 -#bed_shape = 5x5,295x5,295x215,5x215 +#bed_shape = 5x5,295x5,295x220,5x220 #max_print_height = 380 #printer_model = CR5PRO #printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR5PRO\nPRINTER_HAS_BOWDEN From 7fbb3a2b420910e7ce3cabbe44bfcf576b69a176 Mon Sep 17 00:00:00 2001 From: Pascal de Bruijn Date: Mon, 1 Mar 2021 18:40:33 +0100 Subject: [PATCH 033/285] creality.ini: Add CR-5 Pro H this is a high temp variant of the regular CR-5 Pro, with the suspected all metal hotend, presumably the non-H variant has a teflon lined hotend related: 2d762ec3202e9d7e400db4b20e3e5630098f6a19 --- resources/profiles/Creality.ini | 19 +++++++++++++++++- .../profiles/Creality/CR5PROH_thumbnail.png | Bin 0 -> 38706 bytes 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 resources/profiles/Creality/CR5PROH_thumbnail.png diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index f68d067d35..423daef0f0 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -104,6 +104,15 @@ default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @ #bed_texture = cr5pro.svg #default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY +#[printer_model:CR5PROH] +#name = Creality CR-5 Pro H +#variants = 0.4 +#technology = FFF +#family = CR +#bed_model = cr5pro_bed.stl +#bed_texture = cr5pro.svg +#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY + #[printer_model:CR6SE] #name = Creality CR-6 SE #variants = 0.4 @@ -905,12 +914,20 @@ printer_notes = Don't remove the following keywords! These keywords are used in #[printer:Creality CR-5 Pro] #inherits = *common*; *slowabl*; *descendingz* -#retract_length = 3 +#retract_length = 6 #bed_shape = 5x5,295x5,295x220,5x220 #max_print_height = 380 #printer_model = CR5PRO #printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR5PRO\nPRINTER_HAS_BOWDEN +#[printer:Creality CR-5 Pro H] +#inherits = *common*; *slowabl*; *descendingz* +#retract_length = 3 +#bed_shape = 5x5,295x5,295x220,5x220 +#max_print_height = 380 +#printer_model = CR5PROH +#printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR5PROH\nPRINTER_HAS_BOWDEN + #[printer:Creality CR-6 SE] #inherits = *common*; *fastabl*; *pauseprint* #bed_shape = 5x0,230x0,230x235,5x235 diff --git a/resources/profiles/Creality/CR5PROH_thumbnail.png b/resources/profiles/Creality/CR5PROH_thumbnail.png new file mode 100644 index 0000000000000000000000000000000000000000..df2f1b665f2f7a3d7d5e45ff3bb29a4fb674bdfe GIT binary patch literal 38706 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 literal 0 HcmV?d00001 From 5276bd98d7afaf953138d5e46963d9b4fab044c6 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 1 Mar 2021 18:41:36 +0100 Subject: [PATCH 034/285] WIP: MutablePolygon - linked list based polygon implementation allowing rapid insertion and removal of points. WIP: porting smooth_outward() from Cura. --- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/MutablePolygon.cpp | 242 +++++++++++++++++++++++ src/libslic3r/MutablePolygon.hpp | 227 +++++++++++++++++++++ src/libslic3r/Point.hpp | 20 +- tests/libslic3r/CMakeLists.txt | 1 + tests/libslic3r/test_mutable_polygon.cpp | 145 ++++++++++++++ 6 files changed, 632 insertions(+), 5 deletions(-) create mode 100644 src/libslic3r/MutablePolygon.cpp create mode 100644 src/libslic3r/MutablePolygon.hpp create mode 100644 tests/libslic3r/test_mutable_polygon.cpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 1267e216c1..6159b2c5c3 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -145,6 +145,8 @@ add_library(libslic3r STATIC Point.hpp Polygon.cpp Polygon.hpp + MutablePolygon.cpp + MutablePolygon.hpp PolygonTrimmer.cpp PolygonTrimmer.hpp Polyline.cpp diff --git a/src/libslic3r/MutablePolygon.cpp b/src/libslic3r/MutablePolygon.cpp new file mode 100644 index 0000000000..951485259d --- /dev/null +++ b/src/libslic3r/MutablePolygon.cpp @@ -0,0 +1,242 @@ +#include "MutablePolygon.hpp" +#include "Line.hpp" + +namespace Slic3r { + +// Remove exact duplicate points. May reduce the polygon down to empty polygon. +void remove_duplicates(MutablePolygon &polygon) +{ + if (! polygon.empty()) { + auto begin = polygon.begin(); + auto it = begin; + for (++ it; it != begin;) { + auto prev = it.prev(); + if (*prev == *it) + it = it.remove(); + else + ++ it; + } + } +} + +// Remove nearly duplicate points. May reduce the polygon down to empty polygon. +void remove_duplicates(MutablePolygon &polygon, double eps) +{ + if (! polygon.empty()) { + auto eps2 = eps * eps; + auto begin = polygon.begin(); + auto it = begin; + for (++ it; it != begin;) { + auto prev = it.prev(); + if ((*it - *prev).cast().squaredNorm() < eps2) + it = it.remove(); + else + ++ it; + } + } +} + +// Sample a point on line (a, b) at distance "dist" from ref_pt. +// If two points fulfill the condition, then the first one (closer to point a) is taken. +// If none of the two points falls on line (a, b), return false. +template +static inline VectorType point_on_line_at_dist(const VectorType &a, const VectorType &b, const VectorType &ref_pt, const double dist) +{ + using T = typename VectorType::Scalar; + auto v = b - a; + auto l2 = v.squaredNorm(); + assert(l2 > T(0)); + auto vpt = ref_pt - a; + // Parameter of the foot point of ref_pt on line (a, b). + auto t = v.dot(vpt) / l2; + // Foot point of ref_pt on line (a, b). + auto foot_pt = a + t * v; + auto dfoot2 = vpt.squaredNorm() - (foot_pt - ref_pt).squaredNorm(); + // Distance of the result point from the foot point, normalized to length of (a, b). + auto dfoot = dfoot2 > T(0) ? sqrt(dfoot2) / sqrt(l2) : T(0); + auto t_result = t - dfoot; + if (t_result < T(0)) + t_result = t + dfoot; + t_result = Slic3r::clamp(0., 1., t_result); + return a + v * t; +} + +static bool smooth_corner_complex(const Vec2d p1, MutablePolygon::iterator &it0, MutablePolygon::iterator &it2, const double shortcut_length) +{ + // walk away from the corner until the shortcut > shortcut_length or it would smooth a piece inward + // - walk in both directions untill shortcut > shortcut_length + // - stop walking in one direction if it would otherwise cut off a corner in that direction + // - same in the other direction + // - stop if both are cut off + // walk by updating p0_it and p2_it + double shortcut_length2 = shortcut_length * shortcut_length; + bool forward_is_blocked = false; + bool forward_is_too_far = false; + bool backward_is_blocked = false; + bool backward_is_too_far = false; + for (;;) { + const bool forward_has_converged = forward_is_blocked || forward_is_too_far; + const bool backward_has_converged = backward_is_blocked || backward_is_too_far; + if (forward_has_converged && backward_has_converged) { + if (forward_is_too_far && backward_is_too_far && (*it0.prev() - *it2.next()).cast().squaredNorm() < shortcut_length2) { + // Trim the narrowing region. + -- it0; + ++ it2; + forward_is_too_far = false; + backward_is_too_far = false; + continue; + } else + break; + } + + const Vec2d p0 = it0->cast(); + const Vec2d p2 = it2->cast(); + if (! forward_has_converged && (backward_has_converged || (p2 - p1).squaredNorm() < (p0 - p1).squaredNorm())) { + // walk forward + const auto it2_2 = it2.next(); + const Vec2d p2_2 = it2_2->cast(); + if (cross2(p2 - p0, p2_2 - p0) > 0) { + forward_is_blocked = true; + } else if ((p2_2 - p0).squaredNorm() > shortcut_length2) { + forward_is_too_far = true; + } else { + it2 = it2_2; // make one step in the forward direction + backward_is_blocked = false; // invalidate data about backward walking + backward_is_too_far = false; + } + } else { + // walk backward + const auto it0_2 = it0.prev(); + const Vec2d p0_2 = it0_2->cast(); + if (cross2(p0_2 - p0, p2 - p0_2) > 0) { + backward_is_blocked = true; + } else if ((p2 - p0_2).squaredNorm() > shortcut_length2) { + backward_is_too_far = true; + } else { + it0 = it0_2; // make one step in the backward direction + forward_is_blocked = false; // invalidate data about forward walking + forward_is_too_far = false; + } + } + + if (it0.prev() == it2 || it0 == it2) { + // stop if we went all the way around the polygon + // this should only be the case for hole polygons (?) + if (forward_is_too_far && backward_is_too_far) { + // in case p0_it.prev() == p2_it : + // / . + // / /| + // | becomes | | + // \ \| + // \ . + // in case p0_it == p2_it : + // / . + // / becomes /| + // \ \| + // \ . + break; + } else { + // this whole polygon can be removed + return true; + } + } + } + + const Vec2d p0 = it0->cast(); + const Vec2d p2 = it2->cast(); + const Vec2d v02 = p2 - p0; + const int64_t l2_v02 = v02.squaredNorm(); + if (std::abs(l2_v02 - shortcut_length2) < shortcut_length * 10) // i.e. if (size2 < l * (l+10) && size2 > l * (l-10)) + { // v02 is approximately shortcut length + // handle this separately to avoid rounding problems below in the getPointOnLineWithDist function + // p0_it and p2_it are already correct + } else if (! backward_is_blocked && ! forward_is_blocked) { + const auto l_v02 = sqrt(l2_v02); + const Vec2d p0_2 = it0.prev()->cast(); + const Vec2d p2_2 = it2.next()->cast(); + double t = Slic3r::clamp(0., 1., (shortcut_length - l_v02) / ((p2_2 - p0_2).norm() - l_v02)); + it0 = it0.prev().insert((p0 + (p0_2 - p0) * t).cast()); + it2 = it2.insert((p2 + (p2_2 - p2) * t).cast()); + } else if (! backward_is_blocked) { + it0 = it0.prev().insert(point_on_line_at_dist(p0, Vec2d(it0.prev()->cast()), p2, shortcut_length).cast()); + } else if (! forward_is_blocked) { + it2 = it2.insert(point_on_line_at_dist(p2, Vec2d(it2.next()->cast()), p0, shortcut_length).cast()); + } else { + // | + // __|2 + // | / > shortcut cannot be of the desired length + // ___|/ . + // 0 + // both are blocked and p0_it and p2_it are already correct + } + // Delete all the points between it0 and it2. + while (it0.next() != it2) + it0.next().remove(); + return false; +} + +void smooth_outward(MutablePolygon &polygon, double shortcut_length) +{ + remove_duplicates(polygon, scaled(0.01)); + + const int shortcut_length2 = shortcut_length * shortcut_length; + static constexpr const double cos_min_angle = -0.70710678118654752440084436210485; // cos(135 degrees) + + MutablePolygon::iterator it1 = polygon.begin(); + do { + const Vec2d p1 = it1->cast(); + auto it0 = it1.prev(); + auto it2 = it1.next(); + const Vec2d p0 = it0->cast(); + const Vec2d p2 = it2->cast(); + const Vec2d v1 = p0 - p1; + const Vec2d v2 = p2 - p1; + const double cos_angle = v1.dot(v2); + if (cos_angle < cos_min_angle && cross2(v1, v2) < 0) { + // Simplify the sharp angle. + const Vec2d v02 = p2 - p0; + const double l2_v02 = v02.squaredNorm(); + if (l2_v02 >= shortcut_length2) { + // Trim an obtuse corner. + it1.remove(); + if (l2_v02 > Slic3r::sqr(shortcut_length + SCALED_EPSILON)) { + double l2_1 = v1.squaredNorm(); + double l2_2 = v2.squaredNorm(); + bool trim = true; + if (cos_angle > 0.9999) { + // The triangle p0, p1, p2 is likely degenerate. + // Measure height of the triangle. + double d2 = l2_1 > l2_2 ? line_alg::distance_to_squared(Linef{ p0, p1 }, p2) : line_alg::distance_to_squared(Linef{ p2, p1 }, p0); + if (d2 < Slic3r::sqr(scaled(0.02))) + trim = false; + } + if (trim) { + Vec2d bisector = v1 / l2_1 + v2 / l2_2; + double d1 = v1.dot(bisector) / l2_1; + double d2 = v2.dot(bisector) / l2_2; + double lbisector = bisector.norm(); + if (d1 < shortcut_length && d2 < shortcut_length) { + it0.insert((p1 + v1 * (shortcut_length / d1)).cast()) + .insert((p1 + v2 * (shortcut_length / d2)).cast()); + } else if (v1.squaredNorm() < v2.squaredNorm()) + it0.insert(point_on_line_at_dist(p1, p2, p0, shortcut_length).cast()); + else + it0.insert(point_on_line_at_dist(p1, p0, p2, shortcut_length).cast()); + } + } + } else { + bool remove_poly = smooth_corner_complex(p1, it0, it2, shortcut_length); // edits p0_it and p2_it! + if (remove_poly) { + // don't convert ListPolygon into result + return; + } + } + // update: + it1 = it2; // next point to consider for whether it's an internal corner + } + else + ++ it1; + } while (it1 != polygon.begin()); +} + +} // namespace Slic3r diff --git a/src/libslic3r/MutablePolygon.hpp b/src/libslic3r/MutablePolygon.hpp new file mode 100644 index 0000000000..b699a25eda --- /dev/null +++ b/src/libslic3r/MutablePolygon.hpp @@ -0,0 +1,227 @@ +#ifndef slic3r_MutablePolygon_hpp_ +#define slic3r_MutablePolygon_hpp_ + +#include "Point.hpp" +#include "Polygon.hpp" + +namespace Slic3r { + +class MutablePolygon +{ +public: + using IndexType = int32_t; + using PointType = Point; + class const_iterator { + public: + bool operator==(const const_iterator &rhs) const { assert(m_data == rhs.m_data); assert(this->valid()); return m_idx == rhs.m_idx; } + bool operator!=(const const_iterator &rhs) const { return ! (*this == rhs); } + const_iterator& operator--() { assert(this->valid()); m_idx = m_data->at(m_idx).prev; return *this; } + const_iterator operator--(int) { const_iterator result(*this); --(*this); return result; } + const_iterator& operator++() { assert(this->valid()); m_idx = m_data->at(m_idx).next; return *this; } + const_iterator operator++(int) { const_iterator result(*this); ++(*this); return result; } + const_iterator prev() const { assert(this->valid()); return { m_data, m_data->at(m_idx).prev }; } + const_iterator next() const { assert(this->valid()); return { m_data, m_data->at(m_idx).next }; } + bool valid() const { return m_idx >= 0; } + const PointType& operator*() const { return m_data->at(m_idx).point; } + const PointType* operator->() const { return &m_data->at(m_idx).point; } + const MutablePolygon& polygon() const { assert(this->valid()); m_data; } + IndexType size() const { assert(this->valid()); m_data->size(); } + private: + const_iterator(const MutablePolygon *data, IndexType idx) : m_data(data), m_idx(idx) {} + friend class MutablePolygon; + const MutablePolygon *m_data; + IndexType m_idx; + }; + + class iterator { + public: + bool operator==(const iterator &rhs) const { assert(m_data == rhs.m_data); assert(this->valid()); return m_idx == rhs.m_idx; } + bool operator!=(const iterator &rhs) const { return !(*this == rhs); } + iterator& operator--() { assert(this->valid()); m_idx = m_data->at(m_idx).prev; return *this; } + iterator operator--(int) { iterator result(*this); --(*this); return result; } + iterator& operator++() { assert(this->valid()); m_idx = m_data->at(m_idx).next; return *this; } + iterator operator++(int) { iterator result(*this); ++(*this); return result; } + iterator prev() const { assert(this->valid()); return { m_data, m_data->at(m_idx).prev }; } + iterator next() const { assert(this->valid()); return { m_data, m_data->at(m_idx).next }; } + bool valid() const { return m_idx >= 0; } + PointType& operator*() const { return m_data->at(m_idx).point; } + PointType* operator->() const { return &m_data->at(m_idx).point; } + MutablePolygon& polygon() const { assert(this->valid()); m_data; } + IndexType size() const { assert(this->valid()); m_data->size(); } + iterator& remove() { this->m_idx = m_data->remove(*this).m_idx; return *this; } + iterator insert(const PointType pt) const { return m_data->insert(*this, pt); } + private: + iterator(MutablePolygon *data, IndexType idx) : m_data(data), m_idx(idx) {} + friend class MutablePolygon; + MutablePolygon *m_data; + IndexType m_idx; + }; + + MutablePolygon() = default; + MutablePolygon(const Polygon &rhs, size_t reserve = 0) : MutablePolygon(rhs.points.begin(), rhs.points.end(), reserve) {} + MutablePolygon(std::initializer_list rhs, size_t reserve = 0) : MutablePolygon(rhs.begin(), rhs.end(), reserve) {} + + template + MutablePolygon(IT begin, IT end, size_t reserve = 0) { + m_size = IndexType(end - begin); + if (m_size > 0) { + m_head = 0; + m_data.reserve(std::max(m_size, reserve)); + auto i = IndexType(-1); + auto j = IndexType(1); + for (auto it = begin; it != end; ++ it) + m_data.push_back({ *it, i ++, j ++ }); + m_data.front().prev = m_size - 1; + m_data.back ().next = 0; + } + }; + + Polygon polygon() const { + Polygon out; + if (this->valid()) { + out.points.reserve(this->size()); + for (auto it = this->cbegin(); it != this->cend(); ++ it) + out.points.emplace_back(*it); + } + return out; + }; + + bool empty() const { return this->m_size == 0; } + size_t size() const { return this->m_size; } + size_t capacity() const { return this->m_data.capacity(); } + bool valid() const { return this->m_size >= 3; } + + iterator begin() { return { this, m_head }; } + const_iterator cbegin() const { return { this, m_head }; } + const_iterator begin() const { return this->cbegin(); } + // End points to the last item before roll over. This is different from the usual end() concept! + iterator end() { return { this, this->empty() ? -1 : this->at(m_head).prev }; } + const_iterator cend() const { return { this, this->empty() ? -1 : this->at(m_head).prev }; } + const_iterator end() const { return this->cend(); } + + // Returns iterator following the removed element. Returned iterator will become invalid if last point is removed. + // If begin() is removed, then the next element will become the new begin(). + iterator remove(const iterator it) { assert(it.m_data == this); return { this, this->remove(it.m_idx) }; } + // Insert a new point before it. Returns iterator to the newly inserted point. + // begin() will not change, end() may point to the newly inserted point. + iterator insert(const iterator it, const PointType pt) { assert(it.m_data == this); return { this, this->insert(it.m_idx, pt) }; } + +private: + struct LinkedPoint { + PointType point; + IndexType prev; + IndexType next; + }; + std::vector m_data; + // Number of points in the linked list. + IndexType m_size { 0 }; + IndexType m_head { IndexType(-1) }; + // Head of the free list. + IndexType m_head_free { IndexType(-1) }; + + LinkedPoint& at(IndexType i) { return m_data[i]; } + const LinkedPoint& at(IndexType i) const { return m_data[i]; } + + IndexType remove(const IndexType i) { + assert(i >= 0); + assert(m_size > 0); + assert(m_head != -1); + LinkedPoint &lp = this->at(i); + IndexType prev = lp.prev; + IndexType next = lp.next; + lp.next = m_head_free; + m_head_free = i; + if (-- m_size == 0) + m_head = -1; + else if (m_head == i) + m_head = next; + assert(! this->empty() || (prev == i && next == i)); + if (this->empty()) + return IndexType(-1); + this->at(prev).next = next; + this->at(next).prev = prev; + return next; + } + + IndexType insert(const IndexType i, const Point pt) { + assert(i >= 0); + IndexType n; + IndexType j = this->at(i).prev; + if (m_head_free == -1) { + // Allocate a new item. + n = IndexType(m_data.size()); + m_data.push_back({ pt, j, i }); + } else { + n = m_head_free; + LinkedPoint &nlp = this->at(n); + m_head_free = nlp.next; + nlp = { pt, j, i }; + } + this->at(j).next = n; + this->at(i).prev = n; + ++ m_size; + return n; + } + + /* + IndexType insert(const IndexType i, const Point pt) { + assert(i >= 0); + if (this->at(i).point == pt) + return i; + IndexType j = this->at(i).next; + if (this->at(j).point == pt) + return i; + IndexType n; + if (m_head_free == -1) { + // Allocate a new item. + n = IndexType(m_data.size()); + m_data.push_back({ pt, i, j }); + } else { + LinkedPoint &nlp = this->at(m_head_free); + m_head_free = nlp.next; + nlp = { pt, i, j }; + } + this->at(i).next = n; + this->at(j).prev = n; + ++ m_size; + return n; + } + */ +}; + +inline bool operator==(const MutablePolygon &p1, const MutablePolygon &p2) +{ + if (p1.size() != p2.size()) + return false; + if (p1.empty()) + return true; + auto begin = p1.cbegin(); + auto it = begin; + auto it2 = p2.cbegin(); + for (;;) { + if (! (*it == *it2)) + return false; + if (++ it == begin) + return true; + ++ it2; + } +} + +inline bool operator!=(const MutablePolygon &p1, const MutablePolygon &p2) { return ! (p1 == p2); } + +// Remove exact duplicate points. May reduce the polygon down to empty polygon. +void remove_duplicates(MutablePolygon &polygon); +void remove_duplicates(MutablePolygon &polygon, double eps); + +void smooth_outward(MutablePolygon &polygon, double shortcut_length); + +inline Polygon smooth_outward(const Polygon &polygon, double shortcut_length) +{ + MutablePolygon mp(polygon, polygon.size() * 2); + smooth_outward(mp, shortcut_length); + return mp.polygon(); +} + +} + +#endif // slic3r_MutablePolygon_hpp_ diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index d11af8b58c..84b4a825da 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -56,11 +56,21 @@ typedef Eigen::Transform Transform3d inline bool operator<(const Vec2d &lhs, const Vec2d &rhs) { return lhs(0) < rhs(0) || (lhs(0) == rhs(0) && lhs(1) < rhs(1)); } -// One likely does not want to perform the cross product with a 32bit accumulator. -//inline int32_t cross2(const Vec2i32 &v1, const Vec2i32 &v2) { return v1(0) * v2(1) - v1(1) * v2(0); } -inline int64_t cross2(const Vec2i64 &v1, const Vec2i64 &v2) { return v1(0) * v2(1) - v1(1) * v2(0); } -inline float cross2(const Vec2f &v1, const Vec2f &v2) { return v1(0) * v2(1) - v1(1) * v2(0); } -inline double cross2(const Vec2d &v1, const Vec2d &v2) { return v1(0) * v2(1) - v1(1) * v2(0); } +template +int32_t cross2(const Eigen::MatrixBase> &v1, const Eigen::MatrixBase> &v2) = delete; + +template +inline T cross2(const Eigen::MatrixBase> &v1, const Eigen::MatrixBase> &v2) +{ + return v1(0) * v2(1) - v1(1) * v2(0); +} + +template +inline typename Derived::Scalar cross2(const Eigen::MatrixBase &v1, const Eigen::MatrixBase &v2) +{ + static_assert(std::is_same::value, "cross2(): Scalar types of 1st and 2nd operand must be equal."); + return v1(0) * v2(1) - v1(1) * v2(0); +} template inline Eigen::Matrix perp(const Eigen::MatrixBase> &v) { return Eigen::Matrix(- v.y(), v.x()); } diff --git a/tests/libslic3r/CMakeLists.txt b/tests/libslic3r/CMakeLists.txt index 501af0c6f3..f93cf10afd 100644 --- a/tests/libslic3r/CMakeLists.txt +++ b/tests/libslic3r/CMakeLists.txt @@ -11,6 +11,7 @@ add_executable(${_TEST_NAME}_tests test_geometry.cpp test_placeholder_parser.cpp test_polygon.cpp + test_mutable_polygon.cpp test_stl.cpp test_meshsimplify.cpp test_meshboolean.cpp diff --git a/tests/libslic3r/test_mutable_polygon.cpp b/tests/libslic3r/test_mutable_polygon.cpp new file mode 100644 index 0000000000..2214da6ef0 --- /dev/null +++ b/tests/libslic3r/test_mutable_polygon.cpp @@ -0,0 +1,145 @@ +#include + +#include "libslic3r/MutablePolygon.hpp" + +using namespace Slic3r; + +SCENARIO("Iterators", "[MutablePolygon]") { + GIVEN("Polygon with three points") { + Slic3r::MutablePolygon p({ { 0, 0 }, { 0, 1 }, { 1, 0 } }); + WHEN("Iterating upwards") { + auto begin = p.begin(); + auto end = p.end(); + auto it = begin; + THEN("++ it is not equal to begin") { + REQUIRE(++ it != begin); + } THEN("++ it is not equal to end") { + REQUIRE(++ it != end); + } THEN("++ (++ it) is not equal to begin") { + REQUIRE(++ (++ it) != begin); + } THEN("++ (++ it) is equal to end") { + REQUIRE(++ (++ it) == end); + } THEN("++ (++ (++ it)) is equal to begin") { + REQUIRE(++ (++ (++ it)) == begin); + } THEN("++ (++ (++ it)) is not equal to end") { + REQUIRE(++ (++ (++ it)) != end); + } + } + WHEN("Iterating downwards") { + auto begin = p.begin(); + auto end = p.end(); + auto it = begin; + THEN("-- it is not equal to begin") { + REQUIRE(-- it != begin); + } THEN("-- it is equal to end") { + REQUIRE(-- it == end); + } THEN("-- (-- it) is not equal to begin") { + REQUIRE(-- (-- it) != begin); + } THEN("-- (-- it) is not equal to end") { + REQUIRE(-- (-- it) != end); + } THEN("-- (-- (-- it)) is equal to begin") { + REQUIRE(-- (-- (-- it)) == begin); + } THEN("-- (-- (-- it)) is not equal to end") { + REQUIRE(-- (-- (-- it)) != end); + } + } + WHEN("Deleting 1st point") { + auto it_2nd = p.begin().next(); + auto it_3rd = p.end(); + auto it = p.begin().remove(); + THEN("Size is 2") { + REQUIRE(p.size() == 2); + } THEN("p.begin().remove() == it_2nd") { + REQUIRE(it == it_2nd); + } THEN("it_2nd == new begin()") { + REQUIRE(it_2nd == p.begin()); + } + } + WHEN("Deleting 2nd point") { + auto it_1st = p.begin(); + auto it_2nd = it_1st.next(); + auto it_3rd = p.end(); + auto it = it_2nd.remove(); + THEN("Size is 2") { + REQUIRE(p.size() == 2); + REQUIRE(! p.empty()); + } THEN("it_2nd.remove() == it_3rd") { + REQUIRE(it == it_2nd); + } THEN("it_1st == new begin()") { + REQUIRE(it_1st == p.begin()); + } + } + WHEN("Deleting two points") { + p.begin().remove().remove(); + THEN("Size is 1") { + REQUIRE(p.size() == 1); + } THEN("p.begin().next() == p.begin()") { + REQUIRE(p.begin().next() == p.begin()); + } THEN("p.begin().prev() == p.begin()") { + REQUIRE(p.begin().prev() == p.begin()); + } + } + WHEN("Deleting all points") { + auto it = p.begin().remove().remove().remove(); + THEN("Size is 0") { + REQUIRE(p.size() == 0); + REQUIRE(p.empty()); + } THEN("! p.begin().valid()") { + REQUIRE(!p.begin().valid()); + } THEN("last iterator not valid") { + REQUIRE(! it.valid()); + } + } + WHEN("Inserting a point at the beginning") { + p.insert(p.begin(), { 3, 4 }); + THEN("Polygon content is ok") { + REQUIRE(p == MutablePolygon{ { 0, 0 }, { 0, 1 }, { 1, 0 }, { 3, 4 } }); + } + } + WHEN("Inserting a point at the 2nd position") { + p.insert(++ p.begin(), { 3, 4 }); + THEN("Polygon content is ok") { + REQUIRE(p == MutablePolygon{ { 0, 0 }, { 3, 4 }, { 0, 1 }, { 1, 0 } }); + } + } WHEN("Inserting a point after a point was removed") { + size_t capacity = p.capacity(); + THEN("Initial capacity is 3") { + REQUIRE(capacity == 3); + } + p.begin().remove(); + THEN("After removal of the 1st point the capacity is still 3") { + REQUIRE(p.capacity() == 3); + } + THEN("After removal of the 1st point the content is ok") { + REQUIRE(p == MutablePolygon{ { 0, 1 }, { 1, 0 } }); + } + p.insert(p.begin(), { 5, 6 }); + THEN("After insertion at head position the polygon content is ok") { + REQUIRE(p == MutablePolygon{ { 0, 1 }, { 1, 0 }, { 5, 6 } }); + } THEN("and the capacity is still 3") { + REQUIRE(p.capacity() == 3); + } + } + } +} + +SCENARIO("Remove degenerate points from MutablePolygon", "[MutablePolygon]") { + GIVEN("Polygon with duplicate points"){ + Slic3r::MutablePolygon p({ + { 0, 0 }, + { 0, 100 }, { 0, 100 }, { 0, 100 }, + { 0, 150 }, + { 0, 200 }, + { 200, 200 }, + { 180, 200 }, { 180, 200 }, + { 180, 20 }, + { 180, 0 }, + }); + WHEN("Duplicate points are removed") { + remove_duplicates(p); + THEN("Polygon content is ok") { + REQUIRE(p == Slic3r::MutablePolygon{ { 0, 0 }, { 0, 100 }, { 0, 150 }, { 0, 200 }, { 200, 200 }, { 180, 200 }, { 180, 20 }, { 180, 0 } }); + } + } + } +} From 065a129d474e0d684d986fa49f1192dc5f14fbd1 Mon Sep 17 00:00:00 2001 From: Pascal de Bruijn Date: Mon, 1 Mar 2021 18:43:40 +0100 Subject: [PATCH 035/285] creality.ini: remove single_extruder_multi_material_priming from printer section --- resources/profiles/Creality.ini | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index 423daef0f0..b42be55970 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -813,7 +813,6 @@ end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+2, # Intended for printers with dual extruders and a single hotend/nozzle, like the CR-X series [printer:*dualextruder*] single_extruder_multi_material = 1 -single_extruder_multi_material_priming = 0 cooling_tube_length = 5 cooling_tube_retraction = 91.5 extra_loading_move = -2 From 45cbac0b7c165a245bcb2f55c0b5768c5c248566 Mon Sep 17 00:00:00 2001 From: Pascal de Bruijn Date: Mon, 1 Mar 2021 19:00:04 +0100 Subject: [PATCH 036/285] creality.ini: unhide new printers (except CR-X) --- resources/profiles/Creality.ini | 288 ++++++++++++++++---------------- 1 file changed, 144 insertions(+), 144 deletions(-) diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index b42be55970..f3f5836de4 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -41,23 +41,23 @@ bed_model = ender3v2_bed.stl bed_texture = ender3v2.svg default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY -#[printer_model:ENDER3MAX] -#name = Creality Ender-3 Max -#variants = 0.4 -#technology = FFF -#family = ENDER -#bed_model = cr10v2_bed.stl -#bed_texture = cr10spro.svg -#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY +[printer_model:ENDER3MAX] +name = Creality Ender-3 Max +variants = 0.4 +technology = FFF +family = ENDER +bed_model = cr10v2_bed.stl +bed_texture = cr10spro.svg +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY -#[printer_model:ENDER4] -#name = Creality Ender-4 -#variants = 0.4 -#technology = FFF -#family = ENDER -#bed_model = ender3v2_bed.stl -#bed_texture = ender3v2.svg -#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY +[printer_model:ENDER4] +name = Creality Ender-4 +variants = 0.4 +technology = FFF +family = ENDER +bed_model = ender3v2_bed.stl +bed_texture = ender3v2.svg +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:ENDER5] name = Creality Ender-5 @@ -77,14 +77,14 @@ bed_model = ender5plus_bed.stl bed_texture = ender5plus.svg default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY -#[printer_model:ENDER6] -#name = Creality Ender-6 -#variants = 0.4 -#technology = FFF -#family = ENDER -#bed_model = ender6_bed.stl -#bed_texture = ender6.svg -#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY +[printer_model:ENDER6] +name = Creality Ender-6 +variants = 0.4 +technology = FFF +family = ENDER +bed_model = ender6_bed.stl +bed_texture = ender6.svg +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:ENDER2] name = Creality Ender-2 @@ -95,41 +95,41 @@ bed_model = ender2_bed.stl bed_texture = ender2.svg default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY -#[printer_model:CR5PRO] -#name = Creality CR-5 Pro -#variants = 0.4 -#technology = FFF -#family = CR -#bed_model = cr5pro_bed.stl -#bed_texture = cr5pro.svg -#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY +[printer_model:CR5PRO] +name = Creality CR-5 Pro +variants = 0.4 +technology = FFF +family = CR +bed_model = cr5pro_bed.stl +bed_texture = cr5pro.svg +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY -#[printer_model:CR5PROH] -#name = Creality CR-5 Pro H -#variants = 0.4 -#technology = FFF -#family = CR -#bed_model = cr5pro_bed.stl -#bed_texture = cr5pro.svg -#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY +[printer_model:CR5PROH] +name = Creality CR-5 Pro H +variants = 0.4 +technology = FFF +family = CR +bed_model = cr5pro_bed.stl +bed_texture = cr5pro.svg +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY -#[printer_model:CR6SE] -#name = Creality CR-6 SE -#variants = 0.4 -#technology = FFF -#family = CR -#bed_model = cr6se_bed.stl -#bed_texture = cr6se.svg -#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY +[printer_model:CR6SE] +name = Creality CR-6 SE +variants = 0.4 +technology = FFF +family = CR +bed_model = cr6se_bed.stl +bed_texture = cr6se.svg +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY -#[printer_model:CR6MAX] -#name = Creality CR-6 Max -#variants = 0.4 -#technology = FFF -#family = CR -#bed_model = cr10s4_bed.stl -#bed_texture = cr10s4.svg -#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY +[printer_model:CR6MAX] +name = Creality CR-6 Max +variants = 0.4 +technology = FFF +family = CR +bed_model = cr10s4_bed.stl +bed_texture = cr10s4.svg +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR10MINI] name = Creality CR-10 Mini @@ -140,14 +140,14 @@ bed_model = cr10mini_bed.stl bed_texture = cr10mini.svg default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY -#[printer_model:CR10MAX] -#name = Creality CR-10 Max -#variants = 0.4 -#technology = FFF -#family = CR -#bed_model = cr10max_bed.stl -#bed_texture = cr10max.svg -#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY +[printer_model:CR10MAX] +name = Creality CR-10 Max +variants = 0.4 +technology = FFF +family = CR +bed_model = cr10max_bed.stl +bed_texture = cr10max.svg +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR10] name = Creality CR-10 @@ -239,23 +239,23 @@ bed_model = ender3_bed.stl bed_texture = cr20.svg default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY -#[printer_model:CR200B] -#name = Creality CR-200B -#variants = 0.4 -#technology = FFF -#family = CR -#bed_model = cr200b_bed.stl -#bed_texture = cr200b.svg -#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY +[printer_model:CR200B] +name = Creality CR-200B +variants = 0.4 +technology = FFF +family = CR +bed_model = cr200b_bed.stl +bed_texture = cr200b.svg +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY -#[printer_model:CR8] -#name = Creality CR-8 -#variants = 0.4 -#technology = FFF -#family = CR -#bed_model = cr8_bed.stl -#bed_texture = cr8.svg -#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY +[printer_model:CR8] +name = Creality CR-8 +variants = 0.4 +technology = FFF +family = CR +bed_model = cr8_bed.stl +bed_texture = cr8.svg +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 123-3D Jupiter PLA @CREALITY #[printer_model:CRX] #name = Creality CR-X @@ -859,20 +859,20 @@ max_print_height = 250 printer_model = ENDER3V2 printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER3V2\nPRINTER_HAS_BOWDEN -#[printer:Creality Ender-3 Max] -#inherits = *common* -#retract_length = 6 -#bed_shape = 5x5,295x5,295x295,5x295 -#max_print_height = 340 -#printer_model = ENDER3MAX -#printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER3MAX\nPRINTER_HAS_BOWDEN +[printer:Creality Ender-3 Max] +inherits = *common* +retract_length = 6 +bed_shape = 5x5,295x5,295x295,5x295 +max_print_height = 340 +printer_model = ENDER3MAX +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER3MAX\nPRINTER_HAS_BOWDEN -#[printer:Creality Ender-4] -#inherits = *common*; *descendingz* -#bed_shape = 5x0,215x0,215x220,5x220 -#max_print_height = 300 -#printer_model = ENDER4 -#printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER4\nPRINTER_HAS_BOWDEN +[printer:Creality Ender-4] +inherits = *common*; *descendingz* +bed_shape = 5x0,215x0,215x220,5x220 +max_print_height = 300 +printer_model = ENDER4 +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER4\nPRINTER_HAS_BOWDEN [printer:Creality Ender-5] inherits = *common*; *descendingz* @@ -896,12 +896,12 @@ machine_max_feedrate_z = 5 machine_max_feedrate_x = 300 machine_max_feedrate_y = 300 -#[printer:Creality Ender-6] -#inherits = *common*; *descendingz* -#bed_shape = 5x5,255x5,255x255,5x255 -#max_print_height = 400 -#printer_model = ENDER6 -#printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER6\nPRINTER_HAS_BOWDEN +[printer:Creality Ender-6] +inherits = *common*; *descendingz* +bed_shape = 5x5,255x5,255x255,5x255 +max_print_height = 400 +printer_model = ENDER6 +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER6\nPRINTER_HAS_BOWDEN [printer:Creality Ender-2] inherits = *common* @@ -911,35 +911,35 @@ max_print_height = 200 printer_model = ENDER2 printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER2\nPRINTER_HAS_BOWDEN -#[printer:Creality CR-5 Pro] -#inherits = *common*; *slowabl*; *descendingz* -#retract_length = 6 -#bed_shape = 5x5,295x5,295x220,5x220 -#max_print_height = 380 -#printer_model = CR5PRO -#printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR5PRO\nPRINTER_HAS_BOWDEN +[printer:Creality CR-5 Pro] +inherits = *common*; *slowabl*; *descendingz* +retract_length = 6 +bed_shape = 5x5,295x5,295x220,5x220 +max_print_height = 380 +printer_model = CR5PRO +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR5PRO\nPRINTER_HAS_BOWDEN -#[printer:Creality CR-5 Pro H] -#inherits = *common*; *slowabl*; *descendingz* -#retract_length = 3 -#bed_shape = 5x5,295x5,295x220,5x220 -#max_print_height = 380 -#printer_model = CR5PROH -#printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR5PROH\nPRINTER_HAS_BOWDEN +[printer:Creality CR-5 Pro H] +inherits = *common*; *slowabl*; *descendingz* +retract_length = 3 +bed_shape = 5x5,295x5,295x220,5x220 +max_print_height = 380 +printer_model = CR5PROH +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR5PROH\nPRINTER_HAS_BOWDEN -#[printer:Creality CR-6 SE] -#inherits = *common*; *fastabl*; *pauseprint* -#bed_shape = 5x0,230x0,230x235,5x235 -#printer_model = CR6SE -#printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR6SE\nPRINTER_HAS_BOWDEN +[printer:Creality CR-6 SE] +inherits = *common*; *fastabl*; *pauseprint* +bed_shape = 5x0,230x0,230x235,5x235 +printer_model = CR6SE +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR6SE\nPRINTER_HAS_BOWDEN -#[printer:Creality CR-6 Max] -#inherits = *common*; *slowabl* -#retract_length = 6 -#bed_shape = 5x5,395x5,395x395,5x395 -#max_print_height = 400 -#printer_model = CR6MAX -#printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR6MAX\nPRINTER_HAS_BOWDEN +[printer:Creality CR-6 Max] +inherits = *common*; *slowabl* +retract_length = 6 +bed_shape = 5x5,395x5,395x395,5x395 +max_print_height = 400 +printer_model = CR6MAX +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR6MAX\nPRINTER_HAS_BOWDEN [printer:Creality CR-10 Mini] inherits = *common* @@ -949,13 +949,13 @@ max_print_height = 300 printer_model = CR10MINI printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR10MINI\nPRINTER_HAS_BOWDEN -#[printer:Creality CR-10 Max] -#inherits = *common*; *slowabl* -#retract_length = 6 -#bed_shape = 5x5,445x5,445x445,5x445 -#max_print_height = 470 -#printer_model = CR10MAX -#printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR10MAX\nPRINTER_HAS_BOWDEN +[printer:Creality CR-10 Max] +inherits = *common*; *slowabl* +retract_length = 6 +bed_shape = 5x5,445x5,445x445,5x445 +max_print_height = 470 +printer_model = CR10MAX +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR10MAX\nPRINTER_HAS_BOWDEN [printer:Creality CR-10] inherits = *common* @@ -1032,19 +1032,19 @@ retract_length = 4 printer_model = CR20PRO printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR20PRO\nPRINTER_HAS_BOWDEN -#[printer:Creality CR-200B] -#inherits = *common*; *descendingz* -#bed_shape = 5x5,195x5,195x195,5x195 -#max_print_height = 200 -#printer_model = CR200B -#printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR200B\nPRINTER_HAS_BOWDEN +[printer:Creality CR-200B] +inherits = *common*; *descendingz* +bed_shape = 5x5,195x5,195x195,5x195 +max_print_height = 200 +printer_model = CR200B +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR200B\nPRINTER_HAS_BOWDEN -#[printer:Creality CR-8] -#inherits = *common* -#bed_shape = 5x5,215x5,215x215,5x215 -#max_print_height = 210 -#printer_model = CR8 -#printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR8\nPRINTER_HAS_BOWDEN +[printer:Creality CR-8] +inherits = *common* +bed_shape = 5x5,215x5,215x215,5x215 +max_print_height = 210 +printer_model = CR8 +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR8\nPRINTER_HAS_BOWDEN #[printer:Creality CR-X] #inherits = *common*; *dualextruder* From 7eaadf0e47f6edcdccaeef23dc7687d26fc3a174 Mon Sep 17 00:00:00 2001 From: Pascal de Bruijn Date: Mon, 1 Mar 2021 23:56:08 +0100 Subject: [PATCH 037/285] Revert "creality.ini: move Ender-3 specific extruder_clearance" Due to #589. This reverts commit 34202f71e5ab56c5e96fb3c9d9e6262b4cdd1231. --- resources/profiles/Creality.ini | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index f3f5836de4..1c11210dee 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -296,8 +296,8 @@ external_perimeters_first = 0 external_perimeter_extrusion_width = 0.45 external_perimeter_speed = 25 extra_perimeters = 0 -extruder_clearance_height = 50 -extruder_clearance_radius = 70 +extruder_clearance_height = 34 +extruder_clearance_radius = 47 extrusion_width = 0.45 fill_angle = 45 fill_density = 15% @@ -841,8 +841,6 @@ inherits = *common* renamed_from = "Creality ENDER-3" bed_shape = 3x3,228x3,228x228,3x228 max_print_height = 250 -extruder_clearance_height = 34 -extruder_clearance_radius = 47 printer_model = ENDER3 printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER3\nPRINTER_HAS_BOWDEN From 92a2951b234f513769380d4b1417cae36746a72b Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 2 Mar 2021 08:13:36 +0100 Subject: [PATCH 038/285] Fix build caused by missing returns --- src/libslic3r/MutablePolygon.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/MutablePolygon.hpp b/src/libslic3r/MutablePolygon.hpp index b699a25eda..f40b89e748 100644 --- a/src/libslic3r/MutablePolygon.hpp +++ b/src/libslic3r/MutablePolygon.hpp @@ -24,8 +24,8 @@ public: bool valid() const { return m_idx >= 0; } const PointType& operator*() const { return m_data->at(m_idx).point; } const PointType* operator->() const { return &m_data->at(m_idx).point; } - const MutablePolygon& polygon() const { assert(this->valid()); m_data; } - IndexType size() const { assert(this->valid()); m_data->size(); } + const MutablePolygon& polygon() const { assert(this->valid()); return *m_data; } + IndexType size() const { assert(this->valid()); return m_data->size(); } private: const_iterator(const MutablePolygon *data, IndexType idx) : m_data(data), m_idx(idx) {} friend class MutablePolygon; @@ -46,8 +46,8 @@ public: bool valid() const { return m_idx >= 0; } PointType& operator*() const { return m_data->at(m_idx).point; } PointType* operator->() const { return &m_data->at(m_idx).point; } - MutablePolygon& polygon() const { assert(this->valid()); m_data; } - IndexType size() const { assert(this->valid()); m_data->size(); } + MutablePolygon& polygon() const { assert(this->valid()); return *m_data; } + IndexType size() const { assert(this->valid()); return m_data->size(); } iterator& remove() { this->m_idx = m_data->remove(*this).m_idx; return *this; } iterator insert(const PointType pt) const { return m_data->insert(*this, pt); } private: From 2c8f385c7f468c64ba08984f637ab4712a37089f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 2 Mar 2021 10:01:06 +0100 Subject: [PATCH 039/285] 1st installment of g-code window in preview --- src/libslic3r/GCode.cpp | 8 ++ src/libslic3r/GCode/GCodeProcessor.cpp | 6 + src/libslic3r/GCode/GCodeProcessor.hpp | 6 +- src/libslic3r/Technologies.hpp | 1 + src/slic3r/GUI/GCodeViewer.cpp | 161 ++++++++++++++++++++++++- src/slic3r/GUI/GCodeViewer.hpp | 36 ++++++ src/slic3r/GUI/GLCanvas3D.cpp | 19 +++ src/slic3r/GUI/GLCanvas3D.hpp | 5 + src/slic3r/GUI/KBShortcutsDialog.cpp | 3 + src/slic3r/GUI/Plater.cpp | 10 ++ 10 files changed, 251 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index f3bffe3316..27e5b478bd 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -747,8 +747,16 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* re BOOST_LOG_TRIVIAL(debug) << "Start processing gcode, " << log_memory_info(); m_processor.process_file(path_tmp, true, [print]() { print->throw_if_canceled(); }); DoExport::update_print_estimated_times_stats(m_processor, print->m_print_statistics); +#if ENABLE_GCODE_WINDOW + if (result != nullptr) { + *result = std::move(m_processor.extract_result()); + // set the filename to the correct value + result->filename = path; + } +#else if (result != nullptr) *result = std::move(m_processor.extract_result()); +#endif // ENABLE_GCODE_WINDOW BOOST_LOG_TRIVIAL(debug) << "Finished processing gcode, " << log_memory_info(); if (rename_file(path_tmp, path)) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index c4b2a35183..eb5c616a1e 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -6,6 +6,9 @@ #include #include #include +#if ENABLE_GCODE_WINDOW +#include +#endif // ENABLE_GCODE_WINDOW #include #include @@ -976,6 +979,9 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr } // process gcode +#if ENABLE_GCODE_WINDOW + m_result.filename = filename; +#endif // ENABLE_GCODE_WINDOW m_result.id = ++s_result_id; // 1st move must be a dummy move m_result.moves.emplace_back(MoveVertex()); diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 05f9a2ce33..04d4c9eee5 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -334,13 +334,15 @@ namespace Slic3r { std::vector filament; std::string printer; - void reset() - { + void reset() { print = ""; filament = std::vector(); printer = ""; } }; +#if ENABLE_GCODE_WINDOW + std::string filename; +#endif // ENABLE_GCODE_WINDOW unsigned int id; std::vector moves; Pointfs bed_shape; diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 1c21e2f869..e5b6bd9d63 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -99,6 +99,7 @@ #define ENABLE_WARNING_TEXTURE_REMOVAL (1 && ENABLE_2_3_1_ALPHA1) #define ENABLE_GCODE_LINES_ID_IN_H_SLIDER (1 && ENABLE_2_3_1_ALPHA1) #define ENABLE_VALIDATE_CUSTOM_GCODE (1 && ENABLE_2_3_1_ALPHA1) +#define ENABLE_GCODE_WINDOW (1 && ENABLE_2_3_1_ALPHA1) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 71441e037c..2f2632e07f 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -21,6 +21,9 @@ #include #include +#if ENABLE_GCODE_WINDOW +#include +#endif // ENABLE_GCODE_WINDOW #include #include #include @@ -278,7 +281,7 @@ void GCodeViewer::SequentialView::Marker::render() const imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Tool position") + ":"); ImGui::SameLine(); char buf[1024]; - sprintf(buf, "X: %.2f, Y: %.2f, Z: %.2f", m_world_position(0), m_world_position(1), m_world_position(2)); + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", m_world_position(0), m_world_position(1), m_world_position(2)); imgui.text(std::string(buf)); // force extra frame to automatically update window size @@ -295,6 +298,149 @@ void GCodeViewer::SequentialView::Marker::render() const ImGui::PopStyleVar(); } +#if ENABLE_GCODE_WINDOW +const unsigned int GCodeViewer::SequentialView::GCodeWindow::DefaultMaxLinesCount = 20; + +void GCodeViewer::SequentialView::GCodeWindow::load_gcode() +{ + m_gcode.clear(); + if (m_filename.empty()) + return; + + boost::nowide::ifstream f(m_filename); + std::string line; + while (std::getline(f, line)) { + m_gcode.push_back(line); + } + + m_file_size = static_cast(m_gcode.size()); + m_max_lines_count = std::min(DefaultMaxLinesCount, m_file_size); +} + +void GCodeViewer::SequentialView::GCodeWindow::start_mapping_file() +{ + std::cout << "GCodeViewer::SequentialView::GCodeWindow::start_mapping_file()\n"; +} + +void GCodeViewer::SequentialView::GCodeWindow::stop_mapping_file() +{ + std::cout << "GCodeViewer::SequentialView::GCodeWindow::stop_mapping_file()\n"; +} + +void GCodeViewer::SequentialView::GCodeWindow::render(unsigned int curr_line_id) const +{ + static const ImVec4 LINE_NUMBER_COLOR = ImGuiWrapper::COL_ORANGE_LIGHT; + static const ImVec4 COMMAND_COLOR = { 0.8f, 0.8f, 0.0f, 1.0f }; + static const ImVec4 COMMENT_COLOR = { 0.7f, 0.7f, 0.7f, 1.0f }; + + if (!m_visible) + return; + + if (m_filename.empty() || m_gcode.empty()) + return; + + if (curr_line_id == 0) + return; + + // calculate visible range + unsigned int half_max_lines_count = m_max_lines_count / 2; + unsigned int start_id = (curr_line_id >= half_max_lines_count) ? curr_line_id - half_max_lines_count : 0; + unsigned int end_id = start_id + m_max_lines_count - 1; + if (end_id >= m_file_size) { + end_id = m_file_size - 1; + start_id = end_id - m_max_lines_count + 1; + } + + ImGuiWrapper& imgui = *wxGetApp().imgui(); + + imgui.set_next_window_pos(0.0f, 0.5f * wxGetApp().plater()->get_current_canvas3D()->get_canvas_size().get_height(), ImGuiCond_Always, 0.0f, 0.5f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::SetNextWindowBgAlpha(0.6f); + imgui.begin(std::string("G-code"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); + + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + const auto& [id_offset, text_height] = ImGui::CalcTextSize(std::to_string(end_id).c_str()); + + for (unsigned int id = start_id; id <= end_id; ++id) { + const std::string& line = m_gcode[id]; + + std::string command; + std::string parameters; + std::string comment; + + // extract comment + std::vector tokens; + boost::split(tokens, line, boost::is_any_of(";"), boost::token_compress_on); + command = tokens.front(); + if (tokens.size() > 1) + comment = ";" + tokens.back(); + + // extract gcode command and parameters + if (!command.empty()) { + boost::split(tokens, command, boost::is_any_of(" "), boost::token_compress_on); + command = tokens.front(); + if (tokens.size() > 1) { + for (size_t i = 1; i < tokens.size(); ++i) { + parameters += " " + tokens[i]; + } + } + } + + // background rect for the current selected move + if (id == curr_line_id - 2) { + ImVec2 pos = ImGui::GetCursorScreenPos(); + const float window_border_size = ImGui::GetCurrentWindow()->WindowBorderSize; + draw_list->AddRectFilled({ window_border_size, pos.y - 1 }, { ImGui::GetCurrentWindow()->Size.x - window_border_size, pos.y + text_height + 1 }, + ImGui::GetColorU32({ 0.1f, 0.1f, 0.1f, 1.0f })); + } + + // render line number + ImGui::PushStyleColor(ImGuiCol_Text, LINE_NUMBER_COLOR); + const std::string id_str = std::to_string(id + 1); + ImGui::SameLine(0.0f, id_offset - ImGui::CalcTextSize(id_str.c_str()).x); + imgui.text(id_str); + ImGui::PopStyleColor(); + + if (!command.empty() || !comment.empty()) + ImGui::SameLine(); + + // render command + if (!command.empty()) { + ImGui::PushStyleColor(ImGuiCol_Text, COMMAND_COLOR); + imgui.text(command); + ImGui::PopStyleColor(); + } + + // render command parameters + if (!parameters.empty()) { + ImGui::SameLine(0.0f, 0.0f); + imgui.text(parameters); + } + + // render comment + if (!comment.empty()) { + if (!command.empty()) + ImGui::SameLine(0.0f, 0.0f); + ImGui::PushStyleColor(ImGuiCol_Text, COMMENT_COLOR); + imgui.text(comment); + ImGui::PopStyleColor(); + } + + if (id < end_id) + ImGui::NewLine(); + } + + imgui.end(); + ImGui::PopStyleVar(); +} + +void GCodeViewer::SequentialView::render() const +{ + marker.render(); + gcode_window.render(gcode_ids[current.last]); +} +#endif // ENABLE_GCODE_WINDOW + const std::vector GCodeViewer::Extrusion_Role_Colors {{ { 0.75f, 0.75f, 0.75f }, // erNone { 1.00f, 0.90f, 0.30f }, // erPerimeter @@ -393,6 +539,11 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& // release gpu memory, if used reset(); +#if ENABLE_GCODE_WINDOW + m_sequential_view.gcode_window.set_filename(gcode_result.filename); + m_sequential_view.gcode_window.load_gcode(); +#endif // ENABLE_GCODE_WINDOW + load_toolpaths(gcode_result); if (m_layers.empty()) @@ -545,7 +696,9 @@ void GCodeViewer::reset() m_layers_z_range = { 0, 0 }; m_roles = std::vector(); m_time_statistics.reset(); - +#if ENABLE_GCODE_WINDOW + m_sequential_view.gcode_window.reset(); +#endif // ENABLE_GCODE_WINDOW #if ENABLE_GCODE_VIEWER_STATISTICS m_statistics.reset_all(); #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -608,7 +761,11 @@ void GCodeViewer::render() const SequentialView* sequential_view = const_cast(&m_sequential_view); if (sequential_view->current.last != sequential_view->endpoints.last) { sequential_view->marker.set_world_position(sequential_view->current_position); +#if ENABLE_GCODE_WINDOW + sequential_view->render(); +#else sequential_view->marker.render(); +#endif // ENABLE_GCODE_WINDOW } render_shells(); render_legend(); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 290c13d51d..e8f9571fce 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -602,6 +602,29 @@ public: void render() const; }; +#if ENABLE_GCODE_WINDOW + class GCodeWindow + { + static const unsigned int DefaultMaxLinesCount; + bool m_visible{ false }; + unsigned int m_max_lines_count{ DefaultMaxLinesCount }; + unsigned int m_file_size{ 0 }; + std::string m_filename; + std::vector m_gcode; + + public: + void set_filename(const std::string& filename) { m_filename = filename; } + void load_gcode(); + void start_mapping_file(); + void stop_mapping_file(); + void reset() { m_filename.clear(); } + + void toggle_visibility() { m_visible = !m_visible; } + + void render(unsigned int curr_line_id) const; + }; +#endif // ENABLE_GCODE_WINDOW + struct Endpoints { size_t first{ 0 }; @@ -614,9 +637,16 @@ public: Endpoints last_current; Vec3f current_position{ Vec3f::Zero() }; Marker marker; +#if ENABLE_GCODE_WINDOW + GCodeWindow gcode_window; +#endif // ENABLE_GCODE_WINDOW #if ENABLE_GCODE_LINES_ID_IN_H_SLIDER std::vector gcode_ids; #endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER + +#if ENABLE_GCODE_WINDOW + void render() const; +#endif // ENABLE_GCODE_WINDOW }; enum class EViewType : unsigned char @@ -710,6 +740,12 @@ public: void export_toolpaths_to_obj(const char* filename) const; +#if ENABLE_GCODE_WINDOW + void start_mapping_gcode_file() { m_sequential_view.gcode_window.start_mapping_file(); } + void stop_mapping_gcode_file() { m_sequential_view.gcode_window.stop_mapping_file(); } + void toggle_gcode_window_visibility() { m_sequential_view.gcode_window.toggle_visibility(); } +#endif // ENABLE_GCODE_WINDOW + private: void load_toolpaths(const GCodeProcessor::Result& gcode_result); void load_shells(const Print& print, bool initialized); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 0aec25404c..54c4844bed 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1195,6 +1195,9 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) GLCanvas3D::~GLCanvas3D() { +#if ENABLE_GCODE_WINDOW + m_gcode_viewer.stop_mapping_gcode_file(); +#endif // ENABLE_GCODE_WINDOW reset_volumes(); } @@ -2621,6 +2624,10 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) case 'a': { post_event(SimpleEvent(EVT_GLCANVAS_ARRANGE)); break; } case 'B': case 'b': { zoom_to_bed(); break; } +#if ENABLE_GCODE_WINDOW + case 'C': + case 'c': { m_gcode_viewer.toggle_gcode_window_visibility(); m_dirty = true; request_extra_frame(); break; } +#endif // ENABLE_GCODE_WINDOW case 'E': case 'e': { m_labels.show(!m_labels.is_shown()); m_dirty = true; break; } case 'G': @@ -3909,6 +3916,18 @@ void GLCanvas3D::mouse_up_cleanup() m_canvas->ReleaseMouse(); } +#if ENABLE_GCODE_WINDOW +void GLCanvas3D::start_mapping_gcode_file() +{ + m_gcode_viewer.start_mapping_gcode_file(); +} + +void GLCanvas3D::stop_mapping_gcode_file() +{ + m_gcode_viewer.stop_mapping_gcode_file(); +} +#endif // ENABLE_GCODE_WINDOW + bool GLCanvas3D::_is_shown_on_screen() const { return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 10294931fe..990c8c048a 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -785,6 +785,11 @@ public: #endif } +#if ENABLE_GCODE_WINDOW + void start_mapping_gcode_file(); + void stop_mapping_gcode_file(); +#endif // ENABLE_GCODE_WINDOW + private: bool _is_shown_on_screen() const; diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index 04e6769e4c..c1efa324ec 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -202,6 +202,9 @@ void KBShortcutsDialog::fill_shortcuts() { "D", L("Horizontal slider - Move active thumb Right") }, { "X", L("On/Off one layer mode of the vertical slider") }, { "L", L("Show/Hide Legend and Estimated printing time") }, +#if ENABLE_GCODE_WINDOW + { "C", L("Show/Hide G-code window") }, +#endif // ENABLE_GCODE_WINDOW }; m_full_shortcuts.push_back({ { _L("Preview"), "" }, preview_shortcuts }); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 6400bbbcc6..b23d811740 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3289,8 +3289,15 @@ void Plater::priv::set_current_panel(wxPanel* panel) panel_sizer->Layout(); if (current_panel == view3D) { +#if ENABLE_GCODE_WINDOW + if (old_panel == preview) { + preview->get_canvas3d()->stop_mapping_gcode_file(); + preview->get_canvas3d()->unbind_event_handlers(); + } +#else if (old_panel == preview) preview->get_canvas3d()->unbind_event_handlers(); +#endif // ENABLE_GCODE_WINDOW view3D->get_canvas3d()->bind_event_handlers(); @@ -3315,6 +3322,9 @@ void Plater::priv::set_current_panel(wxPanel* panel) view3D->get_canvas3d()->unbind_event_handlers(); preview->get_canvas3d()->bind_event_handlers(); +#if ENABLE_GCODE_WINDOW + preview->get_canvas3d()->start_mapping_gcode_file(); +#endif // ENABLE_GCODE_WINDOW // see: Plater::priv::object_list_changed() // FIXME: it may be better to have a single function making this check and let it be called wherever needed From 28b92215c3dcf9f9c62cce308608a8bd500b456b Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 2 Mar 2021 12:06:11 +0100 Subject: [PATCH 040/285] Follow-up https://github.com/prusa3d/PrusaSlicer/commit/409849d238718b75cf0c5ef264ce1df0cedc5c6e: Added check for visibility for all loaded presets. Added notification about this action. + PresetComboBox: Fixed update() function for physical printers, when some of related presets are invisible. --- src/libslic3r/PresetBundle.cpp | 10 ------ src/slic3r/GUI/Plater.cpp | 51 +++++++++++++++++++++++++++-- src/slic3r/GUI/PresetComboBoxes.cpp | 4 +-- 3 files changed, 51 insertions(+), 14 deletions(-) diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index dc52fdaff8..e7f818d085 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -877,16 +877,6 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool } } - // 3.1) If loaded filaments are invisible/non-instaled, set them as visible - for (const std::string& filament : this->filament_presets) { - Preset* preset = this->filaments.find_preset(filament); - if (preset && !preset->is_visible) { - preset->is_visible = true; - if (preset->name == this->filaments.m_edited_preset.name) - this->filaments.get_selected_preset().is_visible = true; - } - } - // 4) Load the project config values (the per extruder wipe matrix etc). this->project_config.apply_only(config, s_project_options); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index fe67049254..762acfdf1a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2189,9 +2189,56 @@ std::vector Plater::priv::load_files(const std::vector& input_ { if (!config.empty()) { Preset::normalize(config); - wxGetApp().preset_bundle->load_config_model(filename.string(), std::move(config)); + PresetBundle* preset_bundle = wxGetApp().preset_bundle; + preset_bundle->load_config_model(filename.string(), std::move(config)); + { + // After loading of the presets from project, check if they are visible. + // Set them to visible if they are not. + + auto update_selected_preset_visibility = [](PresetCollection& presets, std::vector& names) { + if (!presets.get_selected_preset().is_visible) { + assert(presets.get_selected_preset().name == presets.get_edited_preset().name); + presets.get_selected_preset().is_visible = true; + presets.get_edited_preset().is_visible = true; + names.emplace_back(presets.get_selected_preset().name); + } + }; + + std::vector names; + if (printer_technology == ptFFF) { + update_selected_preset_visibility(preset_bundle->prints, names); + for (const std::string& filament : preset_bundle->filament_presets) { + Preset* preset = preset_bundle->filaments.find_preset(filament); + if (preset && !preset->is_visible) { + preset->is_visible = true; + names.emplace_back(preset->name); + if (preset->name == preset_bundle->filaments.get_edited_preset().name) + preset_bundle->filaments.get_selected_preset().is_visible = true; + } + } + } + else { + update_selected_preset_visibility(preset_bundle->sla_prints, names); + update_selected_preset_visibility(preset_bundle->sla_materials, names); + } + update_selected_preset_visibility(preset_bundle->printers, names); + + preset_bundle->update_compatible(PresetSelectCompatibleType::Never); + + // show notification about temporary instaled presets + if (!names.empty()) { + std::string notif_text = into_u8(_L_PLURAL("The preset below was temporary instaled on active instance of PrusaSlicer", + "The presets below were temporary instaled on active instance of PrusaSlicer", names.size())) + ":"; + for (std::string& name : names) + notif_text += "\n - " + name; + notification_manager->push_notification(NotificationType::CustomNotification, + NotificationManager::NotificationLevel::RegularNotification, notif_text); + } + } + if (printer_technology == ptFFF) - CustomGCode::update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, &wxGetApp().preset_bundle->project_config); + CustomGCode::update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, &preset_bundle->project_config); + // For exporting from the amf/3mf we shouldn't check printer_presets for the containing information about "Print Host upload" wxGetApp().load_current_presets(false); // Update filament colors for the MM-printer profile in the full config diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index da723a36b8..c6a3006b71 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -899,7 +899,7 @@ void PlaterPresetComboBox::update() for (PhysicalPrinterCollection::ConstIterator it = ph_printers.begin(); it != ph_printers.end(); ++it) { for (const std::string& preset_name : it->get_preset_names()) { Preset* preset = m_collection->find_preset(preset_name); - if (!preset) + if (!preset || !preset->is_visible) continue; std::string main_icon_name, bitmap_key = main_icon_name = preset->printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name; wxBitmap* bmp = get_bmp(main_icon_name, wide_icons, main_icon_name); @@ -1086,7 +1086,7 @@ void TabPresetComboBox::update() for (PhysicalPrinterCollection::ConstIterator it = ph_printers.begin(); it != ph_printers.end(); ++it) { for (const std::string& preset_name : it->get_preset_names()) { Preset* preset = m_collection->find_preset(preset_name); - if (!preset) + if (!preset || !preset->is_visible) continue; std::string main_icon_name = preset->printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name; From 91ffd8d50167bc987ce44be93df651b82613a9ba Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 2 Mar 2021 17:00:11 +0100 Subject: [PATCH 041/285] Custom G-code validation - Do not veto setting tab switch when invalid g-code is found --- src/slic3r/GUI/MainFrame.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 6b0dd1ad5a..c1aadb3704 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -549,8 +549,10 @@ void MainFrame::init_tabpanel() wxWindow* panel = m_tabpanel->GetCurrentPage(); if (panel != nullptr) { Tab* tab = dynamic_cast(panel); - if (tab != nullptr && !tab->validate_custom_gcodes()) - evt.Veto(); + if (tab != nullptr) + tab->validate_custom_gcodes(); +// if (tab != nullptr && !tab->validate_custom_gcodes()) +// evt.Veto(); } }); #endif // ENABLE_VALIDATE_CUSTOM_GCODE From f586bb6f9ee0ea97265fe2ee4e6fc6a9c7cdf860 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 3 Mar 2021 09:03:07 +0100 Subject: [PATCH 042/285] Fit G-code window between preview legend and view toolbar --- src/slic3r/GUI/GCodeViewer.cpp | 76 ++++++++++++++++++++++------------ src/slic3r/GUI/GCodeViewer.hpp | 12 +++--- 2 files changed, 57 insertions(+), 31 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 2f2632e07f..ec2bc97975 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -299,8 +299,6 @@ void GCodeViewer::SequentialView::Marker::render() const } #if ENABLE_GCODE_WINDOW -const unsigned int GCodeViewer::SequentialView::GCodeWindow::DefaultMaxLinesCount = 20; - void GCodeViewer::SequentialView::GCodeWindow::load_gcode() { m_gcode.clear(); @@ -314,7 +312,6 @@ void GCodeViewer::SequentialView::GCodeWindow::load_gcode() } m_file_size = static_cast(m_gcode.size()); - m_max_lines_count = std::min(DefaultMaxLinesCount, m_file_size); } void GCodeViewer::SequentialView::GCodeWindow::start_mapping_file() @@ -327,42 +324,52 @@ void GCodeViewer::SequentialView::GCodeWindow::stop_mapping_file() std::cout << "GCodeViewer::SequentialView::GCodeWindow::stop_mapping_file()\n"; } -void GCodeViewer::SequentialView::GCodeWindow::render(unsigned int curr_line_id) const +void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, unsigned int curr_line_id) const { static const ImVec4 LINE_NUMBER_COLOR = ImGuiWrapper::COL_ORANGE_LIGHT; static const ImVec4 COMMAND_COLOR = { 0.8f, 0.8f, 0.0f, 1.0f }; static const ImVec4 COMMENT_COLOR = { 0.7f, 0.7f, 0.7f, 1.0f }; - if (!m_visible) + if (!m_visible || m_filename.empty() || m_gcode.empty() || curr_line_id == 0) return; - if (m_filename.empty() || m_gcode.empty()) + // winodws height + const float text_height = ImGui::CalcTextSize("0").y; + const ImGuiStyle& style = ImGui::GetStyle(); + const float height = bottom - top; + if (height < 2.0f * (text_height + style.ItemSpacing.y + style.WindowBorderSize + style.WindowPadding.y)) return; - if (curr_line_id == 0) - return; + // number of visible lines + const unsigned int lines_count = (height - 2.0f * (style.WindowPadding.y + style.WindowBorderSize) + style.ItemSpacing.y) / (text_height + style.ItemSpacing.y); // calculate visible range - unsigned int half_max_lines_count = m_max_lines_count / 2; - unsigned int start_id = (curr_line_id >= half_max_lines_count) ? curr_line_id - half_max_lines_count : 0; - unsigned int end_id = start_id + m_max_lines_count - 1; + const unsigned int half_lines_count = lines_count / 2; + unsigned int start_id = (curr_line_id >= half_lines_count) ? curr_line_id - half_lines_count : 0; + unsigned int end_id = start_id + lines_count - 1; if (end_id >= m_file_size) { end_id = m_file_size - 1; - start_id = end_id - m_max_lines_count + 1; + start_id = end_id - lines_count + 1; } + const float id_offset = ImGui::CalcTextSize(std::to_string(end_id).c_str()).x; + ImGuiWrapper& imgui = *wxGetApp().imgui(); - imgui.set_next_window_pos(0.0f, 0.5f * wxGetApp().plater()->get_current_canvas3D()->get_canvas_size().get_height(), ImGuiCond_Always, 0.0f, 0.5f); + imgui.set_next_window_pos(0.0f, top, ImGuiCond_Always, 0.0f, 0.0f); + imgui.set_next_window_size(0.0f, height, ImGuiCond_Always); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::SetNextWindowBgAlpha(0.6f); imgui.begin(std::string("G-code"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); ImDrawList* draw_list = ImGui::GetWindowDrawList(); - const auto& [id_offset, text_height] = ImGui::CalcTextSize(std::to_string(end_id).c_str()); + + + float f_lines_count = static_cast(lines_count); + ImGui::ItemSize({ 0.0f, 0.5f * (height - f_lines_count * text_height - (f_lines_count - 1.0f) * style.ItemSpacing.y) }); for (unsigned int id = start_id; id <= end_id; ++id) { - const std::string& line = m_gcode[id]; + const std::string& line = m_gcode[id - 1]; std::string command; std::string parameters; @@ -387,16 +394,17 @@ void GCodeViewer::SequentialView::GCodeWindow::render(unsigned int curr_line_id) } // background rect for the current selected move - if (id == curr_line_id - 2) { - ImVec2 pos = ImGui::GetCursorScreenPos(); - const float window_border_size = ImGui::GetCurrentWindow()->WindowBorderSize; - draw_list->AddRectFilled({ window_border_size, pos.y - 1 }, { ImGui::GetCurrentWindow()->Size.x - window_border_size, pos.y + text_height + 1 }, - ImGui::GetColorU32({ 0.1f, 0.1f, 0.1f, 1.0f })); + if (id == curr_line_id - 1) { + const float pos_y = ImGui::GetCursorScreenPos().y; + const float half_ItemSpacing_y = 0.5f * style.ItemSpacing.y; + draw_list->AddRectFilled({ style.WindowBorderSize, pos_y - half_ItemSpacing_y }, + { ImGui::GetCurrentWindow()->Size.x - style.WindowBorderSize, pos_y + text_height + half_ItemSpacing_y }, + ImGui::GetColorU32({ 0.15f, 0.15f, 0.15f, 1.0f })); } // render line number ImGui::PushStyleColor(ImGuiCol_Text, LINE_NUMBER_COLOR); - const std::string id_str = std::to_string(id + 1); + const std::string id_str = std::to_string(id); ImGui::SameLine(0.0f, id_offset - ImGui::CalcTextSize(id_str.c_str()).x); imgui.text(id_str); ImGui::PopStyleColor(); @@ -434,10 +442,13 @@ void GCodeViewer::SequentialView::GCodeWindow::render(unsigned int curr_line_id) ImGui::PopStyleVar(); } -void GCodeViewer::SequentialView::render() const +void GCodeViewer::SequentialView::render(float legend_height) const { marker.render(); - gcode_window.render(gcode_ids[current.last]); + float bottom = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size().get_height(); + if (wxGetApp().is_editor()) + bottom -= wxGetApp().plater()->get_view_toolbar().get_height(); + gcode_window.render(legend_height, bottom, gcode_ids[current.last]); } #endif // ENABLE_GCODE_WINDOW @@ -758,17 +769,22 @@ void GCodeViewer::render() const glsafe(::glEnable(GL_DEPTH_TEST)); render_toolpaths(); + render_shells(); +#if ENABLE_GCODE_WINDOW + float legend_height = 0.0f; + render_legend(legend_height); +#else + render_legend(); +#endif // ENABLE_GCODE_WINDOW SequentialView* sequential_view = const_cast(&m_sequential_view); if (sequential_view->current.last != sequential_view->endpoints.last) { sequential_view->marker.set_world_position(sequential_view->current_position); #if ENABLE_GCODE_WINDOW - sequential_view->render(); + sequential_view->render(legend_height); #else sequential_view->marker.render(); #endif // ENABLE_GCODE_WINDOW } - render_shells(); - render_legend(); #if ENABLE_GCODE_VIEWER_STATISTICS render_statistics(); #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -3923,7 +3939,11 @@ void GCodeViewer::render_shells() const // glsafe(::glDepthMask(GL_TRUE)); } +#if ENABLE_GCODE_WINDOW +void GCodeViewer::render_legend(float& legend_height) const +#else void GCodeViewer::render_legend() const +#endif // ENABLE_GCODE_WINDOW { if (!m_legend_enabled) return; @@ -4606,6 +4626,10 @@ void GCodeViewer::render_legend() const } } +#if ENABLE_GCODE_WINDOW + legend_height = ImGui::GetCurrentWindow()->Size.y; +#endif // ENABLE_GCODE_WINDOW + imgui.end(); ImGui::PopStyleVar(); } diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index e8f9571fce..0302b28bf3 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -605,9 +605,7 @@ public: #if ENABLE_GCODE_WINDOW class GCodeWindow { - static const unsigned int DefaultMaxLinesCount; - bool m_visible{ false }; - unsigned int m_max_lines_count{ DefaultMaxLinesCount }; + bool m_visible{ true }; unsigned int m_file_size{ 0 }; std::string m_filename; std::vector m_gcode; @@ -621,7 +619,7 @@ public: void toggle_visibility() { m_visible = !m_visible; } - void render(unsigned int curr_line_id) const; + void render(float top, float bottom, unsigned int curr_line_id) const; }; #endif // ENABLE_GCODE_WINDOW @@ -645,7 +643,7 @@ public: #endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER #if ENABLE_GCODE_WINDOW - void render() const; + void render(float legend_height) const; #endif // ENABLE_GCODE_WINDOW }; @@ -752,7 +750,11 @@ private: void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const; void render_toolpaths() const; void render_shells() const; +#if ENABLE_GCODE_WINDOW + void render_legend(float& legend_height) const; +#else void render_legend() const; +#endif // ENABLE_GCODE_WINDOW #if ENABLE_GCODE_VIEWER_STATISTICS void render_statistics() const; #endif // ENABLE_GCODE_VIEWER_STATISTICS From 6a46b71dc1b72834f36e16f1bbf3e13f40e64c1d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 3 Mar 2021 13:53:37 +0100 Subject: [PATCH 043/285] #5843 - GCodeProcessor: added processing of lines G28 --- src/libslic3r/GCode/GCodeProcessor.cpp | 27 ++++++++++++++++++++++++++ src/libslic3r/GCode/GCodeProcessor.hpp | 3 +++ 2 files changed, 30 insertions(+) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 46fd3c5b3b..982a5067ca 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1114,6 +1114,7 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) case 21: { process_G21(line); break; } // Set Units to Millimeters case 22: { process_G22(line); break; } // Firmware controlled retract case 23: { process_G23(line); break; } // Firmware controlled unretract + case 28: { process_G28(line); break; } // Move to origin case 90: { process_G90(line); break; } // Set to Absolute Positioning case 91: { process_G91(line); break; } // Set to Relative Positioning case 92: { process_G92(line); break; } // Set Position @@ -2147,6 +2148,32 @@ void GCodeProcessor::process_G23(const GCodeReader::GCodeLine& line) store_move_vertex(EMoveType::Unretract); } +void GCodeProcessor::process_G28(const GCodeReader::GCodeLine& line) +{ + std::string_view cmd = line.cmd(); + std::string new_line_raw = { cmd.data(), cmd.size() }; + bool found = false; + if (line.has_x()) { + new_line_raw += " X0"; + found = true; + } + if (line.has_y()) { + new_line_raw += " Y0"; + found = true; + } + if (line.has_z()) { + new_line_raw += " Z0"; + found = true; + } + if (!found) + new_line_raw += " X0 Y0 Z0"; + + GCodeReader::GCodeLine new_line; + GCodeReader reader; + reader.parse_line(new_line_raw.c_str(), new_line, [](GCodeReader&, const GCodeReader::GCodeLine&) {}); + process_G1(new_line); +} + void GCodeProcessor::process_G90(const GCodeReader::GCodeLine& line) { m_global_positioning_type = EPositioningType::Absolute; diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index f619864c44..c1497edda2 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -572,6 +572,9 @@ namespace Slic3r { // Firmware controlled Unretract void process_G23(const GCodeReader::GCodeLine& line); + // Move to origin + void process_G28(const GCodeReader::GCodeLine& line); + // Set to Absolute Positioning void process_G90(const GCodeReader::GCodeLine& line); From 5f5de1c81279895be5c62bce524eb0b87998c18c Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 3 Mar 2021 15:04:15 +0100 Subject: [PATCH 044/285] Follow-up to 5276bd98d7afaf953138d5e46963d9b4fab044c6: WIP: MutablePolygon - linked list based polygon implementation allowing rapid insertion and removal of points. WIP: porting smooth_outward() from Cura. --- src/libslic3r/MutablePolygon.cpp | 445 ++++++++++++++--------- src/libslic3r/MutablePolygon.hpp | 146 +++++++- tests/libslic3r/test_mutable_polygon.cpp | 34 ++ 3 files changed, 428 insertions(+), 197 deletions(-) diff --git a/src/libslic3r/MutablePolygon.cpp b/src/libslic3r/MutablePolygon.cpp index 951485259d..f166ce7010 100644 --- a/src/libslic3r/MutablePolygon.cpp +++ b/src/libslic3r/MutablePolygon.cpp @@ -1,5 +1,6 @@ #include "MutablePolygon.hpp" #include "Line.hpp" +#include "libslic3r.h" namespace Slic3r { @@ -36,207 +37,295 @@ void remove_duplicates(MutablePolygon &polygon, double eps) } } -// Sample a point on line (a, b) at distance "dist" from ref_pt. -// If two points fulfill the condition, then the first one (closer to point a) is taken. -// If none of the two points falls on line (a, b), return false. -template -static inline VectorType point_on_line_at_dist(const VectorType &a, const VectorType &b, const VectorType &ref_pt, const double dist) +// Adapted from Cura ConstPolygonRef::smooth_corner_complex() by Tim Kuipers. +// A concave corner at it1 with position p1 has been removed by the caller between it0 and it2, where |p2 - p0| < shortcut_length. +// Now try to close a concave crack by walking left from it0 and right from it2 as long as the new clipping edge is smaller than shortcut_length +// and the new clipping edge is still inside the polygon (it is a diagonal, it does not intersect polygon boundary). +// Once the traversal stops (always at a clipping edge shorter than shortcut_length), the final trapezoid is clipped with a new clipping edge of shortcut_length. +// Return true if a hole was completely closed (degenerated to an empty polygon) or a single CCW triangle was left, which is not to be simplified any further. +// it0, it2 are updated to the final clipping edge. +static bool clip_narrow_corner( + const Vec2i64 p1, + MutablePolygon::iterator &it0, + MutablePolygon::iterator &it2, + MutablePolygon::range &unprocessed_range, + int64_t dist2_current, + const int64_t shortcut_length) { - using T = typename VectorType::Scalar; - auto v = b - a; - auto l2 = v.squaredNorm(); - assert(l2 > T(0)); - auto vpt = ref_pt - a; - // Parameter of the foot point of ref_pt on line (a, b). - auto t = v.dot(vpt) / l2; - // Foot point of ref_pt on line (a, b). - auto foot_pt = a + t * v; - auto dfoot2 = vpt.squaredNorm() - (foot_pt - ref_pt).squaredNorm(); - // Distance of the result point from the foot point, normalized to length of (a, b). - auto dfoot = dfoot2 > T(0) ? sqrt(dfoot2) / sqrt(l2) : T(0); - auto t_result = t - dfoot; - if (t_result < T(0)) - t_result = t + dfoot; - t_result = Slic3r::clamp(0., 1., t_result); - return a + v * t; -} + MutablePolygon &polygon = it0.polygon(); + assert(polygon.size() >= 2); -static bool smooth_corner_complex(const Vec2d p1, MutablePolygon::iterator &it0, MutablePolygon::iterator &it2, const double shortcut_length) -{ - // walk away from the corner until the shortcut > shortcut_length or it would smooth a piece inward - // - walk in both directions untill shortcut > shortcut_length - // - stop walking in one direction if it would otherwise cut off a corner in that direction - // - same in the other direction - // - stop if both are cut off - // walk by updating p0_it and p2_it - double shortcut_length2 = shortcut_length * shortcut_length; - bool forward_is_blocked = false; - bool forward_is_too_far = false; - bool backward_is_blocked = false; - bool backward_is_too_far = false; - for (;;) { - const bool forward_has_converged = forward_is_blocked || forward_is_too_far; - const bool backward_has_converged = backward_is_blocked || backward_is_too_far; - if (forward_has_converged && backward_has_converged) { - if (forward_is_too_far && backward_is_too_far && (*it0.prev() - *it2.next()).cast().squaredNorm() < shortcut_length2) { - // Trim the narrowing region. - -- it0; - ++ it2; - forward_is_too_far = false; - backward_is_too_far = false; - continue; - } else - break; - } + const int64_t shortcut_length2 = sqr(shortcut_length); - const Vec2d p0 = it0->cast(); - const Vec2d p2 = it2->cast(); - if (! forward_has_converged && (backward_has_converged || (p2 - p1).squaredNorm() < (p0 - p1).squaredNorm())) { - // walk forward - const auto it2_2 = it2.next(); - const Vec2d p2_2 = it2_2->cast(); - if (cross2(p2 - p0, p2_2 - p0) > 0) { - forward_is_blocked = true; - } else if ((p2_2 - p0).squaredNorm() > shortcut_length2) { - forward_is_too_far = true; + enum Status { + Free, + Blocked, + Far, + }; + Status forward = Free; + Status backward = Free; + + Vec2i64 p0 = it0->cast(); + Vec2i64 p2 = it2->cast(); + Vec2i64 p02; + Vec2i64 p22; + int64_t dist2_next; + + // As long as there is at least a single triangle left in the polygon. + while (polygon.size() >= 3) { + assert(dist2_current <= shortcut_length2); + if (forward == Far && backward == Far) { + p02 = it0.prev()->cast(); + p22 = it2.next()->cast(); + auto d2 = (p22 - p02).squaredNorm(); + if (d2 <= shortcut_length2) { + // The region was narrow until now and it is still narrow. Trim at both sides. + it0 = unprocessed_range.remove_back(it0).prev(); + it2 = unprocessed_range.remove_front(it2); + if (polygon.size() <= 2) + // A hole degenerated to an empty polygon. + return true; + forward = Free; + backward = Free; + dist2_current = d2; + p0 = p02; + p2 = p22; } else { - it2 = it2_2; // make one step in the forward direction - backward_is_blocked = false; // invalidate data about backward walking - backward_is_too_far = false; + // The region is widening. Stop traversal and trim the final trapezoid. + dist2_next = d2; + break; + } + } else if (forward != Free && backward != Free) + // One of the corners is blocked, the other is blocked or too far. Stop traversal. + break; + // Try to proceed by flipping a diagonal. + // Progress by keeping the distance of the clipping edge end points equal to initial p1. + //FIXME This is an arbitrary condition, maybe a more local condition will be better (take a shorter diagonal?). + if (forward == Free && (backward != Free || (p2 - p1).squaredNorm() < (p0 - p1).cast().squaredNorm())) { + p22 = it2.next()->cast(); + if (cross2(p2 - p0, p22 - p0) > 0) + forward = Blocked; + else { + // New clipping edge lenght. + auto d2 = (p22 - p0).squaredNorm(); + if (d2 > shortcut_length2) { + forward = Far; + dist2_next = d2; + } else { + forward = Free; + // Make one step in the forward direction. + it2 = unprocessed_range.remove_front(it2); + p2 = p22; + dist2_current = d2; + } } } else { - // walk backward - const auto it0_2 = it0.prev(); - const Vec2d p0_2 = it0_2->cast(); - if (cross2(p0_2 - p0, p2 - p0_2) > 0) { - backward_is_blocked = true; - } else if ((p2 - p0_2).squaredNorm() > shortcut_length2) { - backward_is_too_far = true; - } else { - it0 = it0_2; // make one step in the backward direction - forward_is_blocked = false; // invalidate data about forward walking - forward_is_too_far = false; - } - } - - if (it0.prev() == it2 || it0 == it2) { - // stop if we went all the way around the polygon - // this should only be the case for hole polygons (?) - if (forward_is_too_far && backward_is_too_far) { - // in case p0_it.prev() == p2_it : - // / . - // / /| - // | becomes | | - // \ \| - // \ . - // in case p0_it == p2_it : - // / . - // / becomes /| - // \ \| - // \ . - break; - } else { - // this whole polygon can be removed - return true; + assert(backward == Free); + p02 = it0.prev()->cast(); + if (cross2(p0 - p2, p02 - p2) > 0) + backward = Blocked; + else { + // New clipping edge lenght. + auto d2 = (p2 - p02).squaredNorm(); + if (d2 > shortcut_length2) { + backward = Far; + dist2_next = d2; + } else { + backward = Free; + // Make one step in the backward direction. + it0 = unprocessed_range.remove_back(it0).prev(); + p0 = p02; + dist2_current = d2; + } } } } - const Vec2d p0 = it0->cast(); - const Vec2d p2 = it2->cast(); - const Vec2d v02 = p2 - p0; - const int64_t l2_v02 = v02.squaredNorm(); - if (std::abs(l2_v02 - shortcut_length2) < shortcut_length * 10) // i.e. if (size2 < l * (l+10) && size2 > l * (l-10)) - { // v02 is approximately shortcut length - // handle this separately to avoid rounding problems below in the getPointOnLineWithDist function - // p0_it and p2_it are already correct - } else if (! backward_is_blocked && ! forward_is_blocked) { - const auto l_v02 = sqrt(l2_v02); - const Vec2d p0_2 = it0.prev()->cast(); - const Vec2d p2_2 = it2.next()->cast(); - double t = Slic3r::clamp(0., 1., (shortcut_length - l_v02) / ((p2_2 - p0_2).norm() - l_v02)); - it0 = it0.prev().insert((p0 + (p0_2 - p0) * t).cast()); - it2 = it2.insert((p2 + (p2_2 - p2) * t).cast()); - } else if (! backward_is_blocked) { - it0 = it0.prev().insert(point_on_line_at_dist(p0, Vec2d(it0.prev()->cast()), p2, shortcut_length).cast()); - } else if (! forward_is_blocked) { - it2 = it2.insert(point_on_line_at_dist(p2, Vec2d(it2.next()->cast()), p0, shortcut_length).cast()); + if (polygon.size() <= 3) { + // A hole degenerated to an empty polygon, or a tiny triangle remained. + assert(polygon.size() < 3 || (forward == Blocked && backward == Blocked) || (forward == Far && backward == Far)); + if (polygon.size() < 3 || forward == Far) { + assert(polygon.size() < 3 || dist2_current <= shortcut_length2); + polygon.clear(); + } else { + // The remaining triangle is CCW oriented, keep it. + } + return true; + } + + assert(dist2_current <= shortcut_length2); + if ((forward == Blocked && backward == Blocked) || dist2_current > sqr(shortcut_length - int64_t(SCALED_EPSILON))) { + // The crack is filled, keep the last clipping edge. + } else if (dist2_next < sqr(shortcut_length - int64_t(SCALED_EPSILON))) { + // To avoid creating tiny edges. + if (forward == Far) + it0 = unprocessed_range.remove_back(it0).prev(); + if (backward == Far) + it2 = unprocessed_range.remove_front(it2); + if (polygon.size() <= 2) + // A hole degenerated to an empty polygon. + return true; + } else if (forward == Blocked || backward == Blocked) { + // One side is far, the other blocked. + assert(forward == Far || backward == Far); + if (backward == Far) { + // Sort, so we will clip the 1st edge. + std::swap(p0, p2); + std::swap(p02, p22); + } + // Find point on (p0, p02) at distance shortcut_length from p2. + // Circle intersects a line at two points, however because |p2 - p0| < shortcut_length, + // only the second intersection is valid. Because |p2 - p02| > shortcut_length, such + // intersection should always be found on (p0, p02). + const Vec2d v = (p02 - p0).cast(); + const Vec2d d = (p0 - p2).cast(); + const double a = v.squaredNorm(); + const double b = 2. * double(d.dot(v)); + double u = b * b - 4. * a * (d.squaredNorm() - shortcut_length2); + assert(u > 0.); + u = sqrt(u); + double t = (- b + u) / (2. * a); + assert(t > 0. && t < 1.); + (backward == Far ? *it2 : *it0) += (v.cast() * t).cast(); } else { - // | - // __|2 - // | / > shortcut cannot be of the desired length - // ___|/ . - // 0 - // both are blocked and p0_it and p2_it are already correct + // The trapezoid (it0.prev(), it0, it2, it2.next()) is widening. Trim it. + assert(forward == Far && backward == Far); + assert(dist2_next > shortcut_length2); + const double dcurrent = sqrt(double(dist2_current)); + double t = (shortcut_length - dcurrent) / (sqrt(double(dist2_next)) - dcurrent); + assert(t > 0. && t < 1.); + *it0 += ((p02 - p0).cast() * t).cast(); + *it2 += ((p22 - p2).cast() * t).cast(); } - // Delete all the points between it0 and it2. - while (it0.next() != it2) - it0.next().remove(); return false; } -void smooth_outward(MutablePolygon &polygon, double shortcut_length) +// adapted from Cura ConstPolygonRef::smooth_outward() by Tim Kuipers. +void smooth_outward(MutablePolygon &polygon, coord_t clip_dist_scaled) { remove_duplicates(polygon, scaled(0.01)); - const int shortcut_length2 = shortcut_length * shortcut_length; - static constexpr const double cos_min_angle = -0.70710678118654752440084436210485; // cos(135 degrees) + const auto clip_dist_scaled2 = sqr(clip_dist_scaled); + const auto clip_dist_scaled2eps = sqr(clip_dist_scaled + int64_t(SCALED_EPSILON)); + const auto foot_dist_min2 = sqr(SCALED_EPSILON); - MutablePolygon::iterator it1 = polygon.begin(); - do { - const Vec2d p1 = it1->cast(); - auto it0 = it1.prev(); - auto it2 = it1.next(); - const Vec2d p0 = it0->cast(); - const Vec2d p2 = it2->cast(); - const Vec2d v1 = p0 - p1; - const Vec2d v2 = p2 - p1; - const double cos_angle = v1.dot(v2); - if (cos_angle < cos_min_angle && cross2(v1, v2) < 0) { - // Simplify the sharp angle. - const Vec2d v02 = p2 - p0; - const double l2_v02 = v02.squaredNorm(); - if (l2_v02 >= shortcut_length2) { - // Trim an obtuse corner. + // Each source point will be visited exactly once. + MutablePolygon::range unprocessed_range(polygon); + while (! unprocessed_range.empty() && polygon.size() > 2) { + auto it1 = unprocessed_range.process_next(); + auto it0 = it1.prev(); + auto it2 = it1.next(); + const Point p0 = *it0; + const Point p1 = *it1; + const Point p2 = *it2; + const Vec2i64 v1 = (p0 - p1).cast(); + const Vec2i64 v2 = (p2 - p1).cast(); + if (cross2(v1, v2) > 0) { + // Concave corner. + int64_t dot = v1.dot(v2); + auto l2v1 = double(v1.squaredNorm()); + auto l2v2 = double(v2.squaredNorm()); + if (dot > 0 || Slic3r::sqr(double(dot)) * 2. < l2v1 * l2v2) { + // Angle between v1 and v2 bigger than 135 degrees. + // Simplify the sharp angle. + Vec2i64 v02 = (p2 - p0).cast(); + int64_t l2v02 = v02.squaredNorm(); it1.remove(); - if (l2_v02 > Slic3r::sqr(shortcut_length + SCALED_EPSILON)) { - double l2_1 = v1.squaredNorm(); - double l2_2 = v2.squaredNorm(); - bool trim = true; - if (cos_angle > 0.9999) { - // The triangle p0, p1, p2 is likely degenerate. - // Measure height of the triangle. - double d2 = l2_1 > l2_2 ? line_alg::distance_to_squared(Linef{ p0, p1 }, p2) : line_alg::distance_to_squared(Linef{ p2, p1 }, p0); - if (d2 < Slic3r::sqr(scaled(0.02))) - trim = false; - } - if (trim) { - Vec2d bisector = v1 / l2_1 + v2 / l2_2; - double d1 = v1.dot(bisector) / l2_1; - double d2 = v2.dot(bisector) / l2_2; - double lbisector = bisector.norm(); - if (d1 < shortcut_length && d2 < shortcut_length) { - it0.insert((p1 + v1 * (shortcut_length / d1)).cast()) - .insert((p1 + v2 * (shortcut_length / d2)).cast()); - } else if (v1.squaredNorm() < v2.squaredNorm()) - it0.insert(point_on_line_at_dist(p1, p2, p0, shortcut_length).cast()); - else - it0.insert(point_on_line_at_dist(p1, p0, p2, shortcut_length).cast()); + if (l2v02 < clip_dist_scaled2) { + // (p0, p2) is short. + // Clip a sharp concave corner by possibly expanding the trimming region left of it0 and right of it2. + // Updates it0, it2 and num_to_process. + if (clip_narrow_corner(p1.cast(), it0, it2, unprocessed_range, l2v02, clip_dist_scaled)) + // Trimmed down to an empty polygon or to a single CCW triangle. + return; + } else { + // Clip an obtuse corner. + if (l2v02 > clip_dist_scaled2eps) { + Vec2d v1d = v1.cast(); + Vec2d v2d = v2.cast(); + // Sort v1d, v2d, shorter first. + bool swap = l2v1 > l2v2; + if (swap) { + std::swap(v1d, v2d); + std::swap(l2v1, l2v2); + } + double lv1 = sqrt(l2v1); + double lv2 = sqrt(l2v2); + // Bisector between v1 and v2. + Vec2d bisector = v1d / lv1 + v2d / lv2; + double l2bisector = bisector.squaredNorm(); + // Squared distance of the end point of v1 to the bisector. + double d2 = l2v1 - sqr(v1d.dot(bisector)) / l2bisector; + if (d2 < foot_dist_min2) { + // Height of the p1, p0, p2 triangle is tiny. Just remove p1. + } else if (d2 < 0.25 * clip_dist_scaled2 + SCALED_EPSILON) { + // The shorter vector is too close to the bisector. Trim the shorter vector fully, + // trim the longer vector partially. + // Intersection of a circle at p2 of radius = clip_dist_scaled + // with a ray (p1, p0), take the intersection after the foot point. + // The intersection shall always exist because |p2 - p1| > clip_dist_scaled. + const double b = - 2. * v1d.cast().dot(v2d); + double u = b * b - 4. * l2v2 * (double(l2v1) - clip_dist_scaled2); + assert(u > 0.); + // Take the second intersection along v2. + double t = (- b + sqrt(u)) / (2. * l2v2); + assert(t > 0. && t < 1.); + Point pt_new = p1 + (t * v2d).cast(); +#ifndef NDEBUG + double d2new = (pt_new - (swap ? p2 : p0)).cast().squaredNorm(); + assert(std::abs(d2new - clip_dist_scaled2) < sqr(10. * SCALED_EPSILON)); +#endif // NDEBUG + it2.insert(pt_new); + } else { + // Cut the corner with a line perpendicular to the bisector. + double t = sqrt(0.25 * clip_dist_scaled2 / d2); + assert(t > 0. && t < 1.); + Point p0 = p1 + (v1d * t).cast(); + Point p2 = p1 + (v2d * (t * lv2 / lv1)).cast(); + if (swap) + std::swap(p0, p2); + it2.insert(p2).insert(p0); + } + } else { + // Just remove p1. + assert(l2v02 >= clip_dist_scaled2 && l2v02 <= clip_dist_scaled2eps); } } - } else { - bool remove_poly = smooth_corner_complex(p1, it0, it2, shortcut_length); // edits p0_it and p2_it! - if (remove_poly) { - // don't convert ListPolygon into result - return; - } - } - // update: - it1 = it2; // next point to consider for whether it's an internal corner - } - else + it1 = it2; + } else + ++ it1; + } else ++ it1; - } while (it1 != polygon.begin()); + } + + if (polygon.size() == 3) { + // Check whether the last triangle is clockwise oriented (it is a hole) and its height is below clip_dist_scaled. + // If so, fill in the hole. + const Point p0 = *polygon.begin().prev(); + const Point p1 = *polygon.begin(); + const Point p2 = *polygon.begin().next(); + Vec2i64 v1 = (p0 - p1).cast(); + Vec2i64 v2 = (p2 - p1).cast(); + if (cross2(v1, v2) > 0) { + // CW triangle. Measure its height. + const Vec2i64 v3 = (p2 - p0).cast(); + int64_t l12 = v1.squaredNorm(); + int64_t l22 = v2.squaredNorm(); + int64_t l32 = v3.squaredNorm(); + if (l22 > l12 && l22 > l32) { + std::swap(v1, v2); + std::swap(l12, l22); + } else if (l32 > l12 && l32 > l22) { + v1 = v3; + l12 = l32; + } + auto h2 = l22 - sqr(double(v1.dot(v2))) / double(l12); + if (h2 < clip_dist_scaled2) + // CW triangle with a low height. Close the hole. + polygon.clear(); + } + } else if (polygon.size() < 3) + polygon.clear(); } } // namespace Slic3r diff --git a/src/libslic3r/MutablePolygon.hpp b/src/libslic3r/MutablePolygon.hpp index f40b89e748..a601f19e3c 100644 --- a/src/libslic3r/MutablePolygon.hpp +++ b/src/libslic3r/MutablePolygon.hpp @@ -6,6 +6,10 @@ namespace Slic3r { +// Polygon implemented as a loop of double linked elements. +// All elements are allocated in a single std::vector<>, thus integer indices are used for +// referencing the previous and next element and inside iterators to survive reallocation +// of the vector. class MutablePolygon { public: @@ -55,6 +59,69 @@ public: friend class MutablePolygon; MutablePolygon *m_data; IndexType m_idx; + friend class range; + }; + + // Iterator range for maintaining a range of unprocessed items, see smooth_outward(). + class range + { + public: + range(MutablePolygon& poly) : range(poly.begin(), poly.end()) {} + range(MutablePolygon::iterator begin, MutablePolygon::iterator end) : m_begin(begin), m_end(end) {} + + // Start of a range, inclusive. If range is empty, then ! begin().valid(). + MutablePolygon::iterator begin() const { return m_begin; } + // End of a range, inclusive. If range is empty, then ! end().valid(). + MutablePolygon::iterator end() const { return m_end; } + // Is the range empty? + bool empty() const { return !m_begin.valid(); } + + // Return begin() and shorten the range by advancing front. + MutablePolygon::iterator process_next() { + assert(!this->empty()); + MutablePolygon::iterator out = m_begin; + this->advance_front(); + return out; + } + + void advance_front() { + assert(!this->empty()); + if (m_begin == m_end) + this->make_empty(); + else + ++ m_begin; + } + + void retract_back() { + assert(!this->empty()); + if (m_begin == m_end) + this->make_empty(); + else + -- m_end; + } + + MutablePolygon::iterator remove_front(MutablePolygon::iterator it) { + if (m_begin == it) + this->advance_front(); + return it.remove(); + } + + MutablePolygon::iterator remove_back(MutablePolygon::iterator it) { + if (m_end == it) + this->retract_back(); + return it.remove(); + } + + private: + // Range from begin to end, inclusive. + // If the range is valid, then both m_begin and m_end are invalid. + MutablePolygon::iterator m_begin; + MutablePolygon::iterator m_end; + + void make_empty() { + m_begin.m_idx = -1; + m_end.m_idx = -1; + } }; MutablePolygon() = default; @@ -63,26 +130,35 @@ public: template MutablePolygon(IT begin, IT end, size_t reserve = 0) { - m_size = IndexType(end - begin); - if (m_size > 0) { - m_head = 0; - m_data.reserve(std::max(m_size, reserve)); - auto i = IndexType(-1); - auto j = IndexType(1); - for (auto it = begin; it != end; ++ it) - m_data.push_back({ *it, i ++, j ++ }); - m_data.front().prev = m_size - 1; - m_data.back ().next = 0; + this->assign_inner(begin, end, reserve); + }; + + template + void assign(IT begin, IT end, size_t reserve = 0) { + m_data.clear(); + m_head = IndexType(-1); + m_head_free = { IndexType(-1) }; + this->assign_inner(begin, end, reserve); + }; + + void assign(const Polygon &rhs, size_t reserve = 0) { + assign(rhs.points.begin(), rhs.points.end(), reserve); + } + + void polygon(Polygon &out) const { + out.points.clear(); + if (this->valid()) { + out.points.reserve(this->size()); + auto it = this->cbegin(); + out.points.emplace_back(*it); + for (++ it; it != this->cbegin(); ++ it) + out.points.emplace_back(*it); } }; Polygon polygon() const { Polygon out; - if (this->valid()) { - out.points.reserve(this->size()); - for (auto it = this->cbegin(); it != this->cend(); ++ it) - out.points.emplace_back(*it); - } + this->polygon(out); return out; }; @@ -90,6 +166,7 @@ public: size_t size() const { return this->m_size; } size_t capacity() const { return this->m_data.capacity(); } bool valid() const { return this->m_size >= 3; } + void clear() { m_data.clear(); m_size = 0; m_head = IndexType(-1); m_head_free = IndexType(-1); } iterator begin() { return { this, m_head }; } const_iterator cbegin() const { return { this, m_head }; } @@ -108,8 +185,11 @@ public: private: struct LinkedPoint { + // 8 bytes PointType point; + // 4 bytes IndexType prev; + // 4 bytes IndexType next; }; std::vector m_data; @@ -122,6 +202,21 @@ private: LinkedPoint& at(IndexType i) { return m_data[i]; } const LinkedPoint& at(IndexType i) const { return m_data[i]; } + template + void assign_inner(IT begin, IT end, size_t reserve) { + m_size = IndexType(end - begin); + if (m_size > 0) { + m_head = 0; + m_data.reserve(std::max(m_size, reserve)); + auto i = IndexType(-1); + auto j = IndexType(1); + for (auto it = begin; it != end; ++ it) + m_data.push_back({ *it, i ++, j ++ }); + m_data.front().prev = m_size - 1; + m_data.back ().next = 0; + } + }; + IndexType remove(const IndexType i) { assert(i >= 0); assert(m_size > 0); @@ -213,13 +308,26 @@ inline bool operator!=(const MutablePolygon &p1, const MutablePolygon &p2) { ret void remove_duplicates(MutablePolygon &polygon); void remove_duplicates(MutablePolygon &polygon, double eps); -void smooth_outward(MutablePolygon &polygon, double shortcut_length); +void smooth_outward(MutablePolygon &polygon, coord_t clip_dist_scaled); -inline Polygon smooth_outward(const Polygon &polygon, double shortcut_length) +inline Polygon smooth_outward(Polygon polygon, coord_t clip_dist_scaled) { MutablePolygon mp(polygon, polygon.size() * 2); - smooth_outward(mp, shortcut_length); - return mp.polygon(); + smooth_outward(mp, clip_dist_scaled); + mp.polygon(polygon); + return polygon; +} + +inline Polygons smooth_outward(Polygons polygons, coord_t clip_dist_scaled) +{ + MutablePolygon mp; + for (Polygon &polygon : polygons) { + mp.assign(polygon, polygon.size() * 2); + smooth_outward(mp, clip_dist_scaled); + mp.polygon(polygon); + } + polygons.erase(std::remove_if(polygons.begin(), polygons.end(), [](const auto &p){ return p.empty(); }), polygons.end()); + return polygons; } } diff --git a/tests/libslic3r/test_mutable_polygon.cpp b/tests/libslic3r/test_mutable_polygon.cpp index 2214da6ef0..238e3604fb 100644 --- a/tests/libslic3r/test_mutable_polygon.cpp +++ b/tests/libslic3r/test_mutable_polygon.cpp @@ -1,5 +1,6 @@ #include +#include "libslic3r/Point.hpp" #include "libslic3r/MutablePolygon.hpp" using namespace Slic3r; @@ -143,3 +144,36 @@ SCENARIO("Remove degenerate points from MutablePolygon", "[MutablePolygon]") { } } } + +SCENARIO("smooth_outward", "[MutablePolygon]") { + GIVEN("Convex polygon") { + MutablePolygon p{ { 0, 0 }, { scaled(10.), 0 }, { 0, scaled(10.) } }; + WHEN("smooth_outward") { + MutablePolygon p2{ p }; + smooth_outward(p2, scaled(10.)); + THEN("Polygon is unmodified") { + REQUIRE(p == p2); + } + } + } + GIVEN("Sharp tiny concave polygon (hole)") { + MutablePolygon p{ { 0, 0 }, { 0, scaled(5.) }, { scaled(10.), 0 } }; + WHEN("smooth_outward") { + MutablePolygon p2{ p }; + smooth_outward(p2, scaled(10.)); + THEN("Hole is closed") { + REQUIRE(p2.empty()); + } + } + } + GIVEN("Two polygons") { + Polygons p{ { { 0, 0 }, { scaled(10.), 0 }, { 0, scaled(10.) } }, + { { 0, 0 }, { 0, scaled(5.) }, { scaled(10.), 0 } } }; + WHEN("smooth_outward") { + p = smooth_outward(p, scaled(10.)); + THEN("CCW contour unmodified, CW contour removed.") { + REQUIRE(p == Polygons{ { { 0, 0 }, { scaled(10.), 0 }, { 0, scaled(10.) } } }); + } + } + } +} From d99895805ce50534f5ba9c28c926e646f0031ac9 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 3 Mar 2021 15:17:45 +0100 Subject: [PATCH 045/285] Follow-up of 6a46b71dc1b72834f36e16f1bbf3e13f40e64c1d - Fix build on non-Windows platforms --- src/libslic3r/GCode/GCodeProcessor.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 982a5067ca..73ad254969 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -2168,10 +2168,10 @@ void GCodeProcessor::process_G28(const GCodeReader::GCodeLine& line) if (!found) new_line_raw += " X0 Y0 Z0"; - GCodeReader::GCodeLine new_line; + GCodeReader::GCodeLine new_gline; GCodeReader reader; - reader.parse_line(new_line_raw.c_str(), new_line, [](GCodeReader&, const GCodeReader::GCodeLine&) {}); - process_G1(new_line); + reader.parse_line(new_line_raw, [&](GCodeReader& reader, const GCodeReader::GCodeLine& gline) { new_gline = gline; }); + process_G1(new_gline); } void GCodeProcessor::process_G90(const GCodeReader::GCodeLine& line) From a7255235e5e010a545d68f3932efc11c33e5f216 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 3 Mar 2021 15:19:24 +0100 Subject: [PATCH 046/285] Remove gcc warning about uninitialized values --- src/libslic3r/SLA/IndexedMesh.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/SLA/IndexedMesh.cpp b/src/libslic3r/SLA/IndexedMesh.cpp index 485fa98ed8..2f47c63870 100644 --- a/src/libslic3r/SLA/IndexedMesh.cpp +++ b/src/libslic3r/SLA/IndexedMesh.cpp @@ -122,7 +122,7 @@ IndexedMesh::hit_result IndexedMesh::query_ray_hit(const Vec3d &s, const Vec3d &dir) const { assert(is_approx(dir.norm(), 1.)); - igl::Hit hit; + igl::Hit hit{-1, -1, 0.f, 0.f, 0.f}; hit.t = std::numeric_limits::infinity(); #ifdef SLIC3R_HOLE_RAYCASTER From a820e8c22fdb9aec26f43c25a0220616c4309238 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 4 Mar 2021 08:37:21 +0100 Subject: [PATCH 047/285] Vertically center text in G-code window --- src/slic3r/GUI/GCodeViewer.cpp | 40 +++++++++++++++++----------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index c8463e2d04..6d59866909 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -329,23 +329,24 @@ void GCodeViewer::SequentialView::GCodeWindow::stop_mapping_file() void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, unsigned int curr_line_id) const { static const ImVec4 LINE_NUMBER_COLOR = ImGuiWrapper::COL_ORANGE_LIGHT; + static const ImVec4 HIGHLIGHT_RECT_COLOR = ImGuiWrapper::COL_ORANGE_DARK; static const ImVec4 COMMAND_COLOR = { 0.8f, 0.8f, 0.0f, 1.0f }; static const ImVec4 COMMENT_COLOR = { 0.7f, 0.7f, 0.7f, 1.0f }; if (!m_visible || m_filename.empty() || m_gcode.empty() || curr_line_id == 0) return; - // winodws height + // window height const float text_height = ImGui::CalcTextSize("0").y; const ImGuiStyle& style = ImGui::GetStyle(); - const float height = bottom - top; - if (height < 2.0f * (text_height + style.ItemSpacing.y + style.WindowBorderSize + style.WindowPadding.y)) + const float wnd_height = bottom - top; + if (wnd_height < 2.0f * (text_height + style.ItemSpacing.y + style.WindowPadding.y)) return; // number of visible lines - const unsigned int lines_count = (height - 2.0f * (style.WindowPadding.y + style.WindowBorderSize) + style.ItemSpacing.y) / (text_height + style.ItemSpacing.y); + const unsigned int lines_count = (wnd_height - 2.0f * style.WindowPadding.y + style.ItemSpacing.y) / (text_height + style.ItemSpacing.y); - // calculate visible range + // visible range const unsigned int half_lines_count = lines_count / 2; unsigned int start_id = (curr_line_id >= half_lines_count) ? curr_line_id - half_lines_count : 0; unsigned int end_id = start_id + lines_count - 1; @@ -354,21 +355,21 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u start_id = end_id - lines_count + 1; } - const float id_offset = ImGui::CalcTextSize(std::to_string(end_id).c_str()).x; + // line id number column width + const float id_width = ImGui::CalcTextSize(std::to_string(end_id).c_str()).x; ImGuiWrapper& imgui = *wxGetApp().imgui(); imgui.set_next_window_pos(0.0f, top, ImGuiCond_Always, 0.0f, 0.0f); - imgui.set_next_window_size(0.0f, height, ImGuiCond_Always); + imgui.set_next_window_size(0.0f, wnd_height, ImGuiCond_Always); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::SetNextWindowBgAlpha(0.6f); imgui.begin(std::string("G-code"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); ImDrawList* draw_list = ImGui::GetWindowDrawList(); - - float f_lines_count = static_cast(lines_count); - ImGui::ItemSize({ 0.0f, 0.5f * (height - f_lines_count * text_height - (f_lines_count - 1.0f) * style.ItemSpacing.y) }); + const float f_lines_count = static_cast(lines_count); + ImGui::SetCursorPosY({ 0.5f * (wnd_height - f_lines_count * text_height - (f_lines_count - 1.0f) * style.ItemSpacing.y) }); for (unsigned int id = start_id; id <= end_id; ++id) { const std::string& line = m_gcode[id - 1]; @@ -395,19 +396,21 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u } } - // background rect for the current selected move - if (id == curr_line_id - 1) { + // rect for the current selected move + if (id == curr_line_id) { const float pos_y = ImGui::GetCursorScreenPos().y; const float half_ItemSpacing_y = 0.5f * style.ItemSpacing.y; - draw_list->AddRectFilled({ style.WindowBorderSize, pos_y - half_ItemSpacing_y }, - { ImGui::GetCurrentWindow()->Size.x - style.WindowBorderSize, pos_y + text_height + half_ItemSpacing_y }, - ImGui::GetColorU32({ 0.15f, 0.15f, 0.15f, 1.0f })); + const float half_padding_x = 0.5f * style.WindowPadding.x; + draw_list->AddRect({ half_padding_x, pos_y - half_ItemSpacing_y }, + { ImGui::GetCurrentWindow()->Size.x - half_padding_x, pos_y + text_height + half_ItemSpacing_y }, + ImGui::GetColorU32(HIGHLIGHT_RECT_COLOR)); } // render line number - ImGui::PushStyleColor(ImGuiCol_Text, LINE_NUMBER_COLOR); const std::string id_str = std::to_string(id); - ImGui::SameLine(0.0f, id_offset - ImGui::CalcTextSize(id_str.c_str()).x); + ImGui::Dummy({ id_width - ImGui::CalcTextSize(id_str.c_str()).x, text_height }); + ImGui::SameLine(0.0f, 0.0f); + ImGui::PushStyleColor(ImGuiCol_Text, LINE_NUMBER_COLOR); imgui.text(id_str); ImGui::PopStyleColor(); @@ -435,9 +438,6 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u imgui.text(comment); ImGui::PopStyleColor(); } - - if (id < end_id) - ImGui::NewLine(); } imgui.end(); From abd5a9a46e16bab66988bf338516b1e0d1f5b4e8 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 26 Feb 2021 08:23:37 +0100 Subject: [PATCH 048/285] Add a notification when custom support enforcers are not used due to supports being off It is now emitted from Print::validate and has a hyperlink to enable supports --- src/libslic3r/Print.cpp | 19 ++++++++- src/libslic3r/Print.hpp | 2 +- src/libslic3r/PrintBase.hpp | 2 +- src/libslic3r/PrintObject.cpp | 18 +++++++++ src/libslic3r/SLAPrint.cpp | 2 +- src/libslic3r/SLAPrint.hpp | 2 +- src/slic3r/GUI/BackgroundSlicingProcess.cpp | 4 +- src/slic3r/GUI/BackgroundSlicingProcess.hpp | 2 +- src/slic3r/GUI/NotificationManager.hpp | 4 +- src/slic3r/GUI/Plater.cpp | 45 ++++++++++++++++++++- 10 files changed, 89 insertions(+), 11 deletions(-) diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 643cc9a970..89a8dc77b3 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1245,7 +1245,7 @@ static inline bool sequential_print_vertical_clearance_valid(const Print &print) } // Precondition: Print::validate() requires the Print::apply() to be called its invocation. -std::string Print::validate() const +std::string Print::validate(std::string* warning) const { if (m_objects.empty()) return L("All objects are outside of the print volume."); @@ -1440,7 +1440,22 @@ std::string Print::validate() const } } } - + + // Do we have custom support data that would not be used? + // Notify the user in that case. + if (! object->has_support() && warning) { + for (const ModelVolume* mv : object->model_object()->volumes) { + bool has_enforcers = mv->is_support_enforcer() + || (mv->is_model_part() + && ! mv->supported_facets.empty() + && ! mv->supported_facets.get_facets(*mv, EnforcerBlockerType::ENFORCER).indices.empty()); + if (has_enforcers) { + *warning = "_SUPPORTS_OFF"; + break; + } + } + } + // validate first_layer_height double first_layer_height = object->config().get_abs_value("first_layer_height"); double first_layer_min_nozzle_diameter; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index e8e81a5295..9f22ddf133 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -444,7 +444,7 @@ public: bool has_brim() const; // Returns an empty string if valid, otherwise returns an error message. - std::string validate() const override; + std::string validate(std::string* warning = nullptr) const override; double skirt_first_layer_height() const; Flow brim_flow() const; Flow skirt_flow() const; diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp index 2aff13ae9c..e0aa56ba5a 100644 --- a/src/libslic3r/PrintBase.hpp +++ b/src/libslic3r/PrintBase.hpp @@ -366,7 +366,7 @@ public: virtual std::vector print_object_ids() const = 0; // Validate the print, return empty string if valid, return error if process() cannot (or should not) be started. - virtual std::string validate() const { return std::string(); } + virtual std::string validate(std::string* warning = nullptr) const { return std::string(); } enum ApplyStatus { // No change after the Print::apply() call. diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index c963418c30..52bdc87e7d 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -430,6 +430,24 @@ void PrintObject::generate_support_material() if (layer->empty()) throw Slic3r::SlicingError("Levitating objects cannot be printed without supports."); #endif + + // Do we have custom support data that would not be used? + // Notify the user in that case. + if (! this->has_support()) { + for (const ModelVolume* mv : this->model_object()->volumes) { + bool has_enforcers = mv->is_support_enforcer() + || (mv->is_model_part() + && ! mv->supported_facets.empty() + && ! mv->supported_facets.get_facets(*mv, EnforcerBlockerType::ENFORCER).indices.empty()); + if (has_enforcers) { + this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, + L("An object has custom support enforcers which will not be used " + "because supports are off. Consider turning them on.") + "\n" + + (L("Object name")) + ": " + this->model_object()->name); + break; + } + } + } } this->set_done(posSupportMaterial); } diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 65fac73f34..16b068cb90 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -617,7 +617,7 @@ std::string SLAPrint::output_filename(const std::string &filename_base) const return this->PrintBase::output_filename(m_print_config.output_filename_format.value, ".sl1", filename_base, &config); } -std::string SLAPrint::validate() const +std::string SLAPrint::validate(std::string*) const { for(SLAPrintObject * po : m_objects) { diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 742670e2cb..bed66ab4f4 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -458,7 +458,7 @@ public: const SLAPrintStatistics& print_statistics() const { return m_print_statistics; } - std::string validate() const override; + std::string validate(std::string* warning = nullptr) const override; // An aggregation of SliceRecord-s from all the print objects for each // occupied layer. Slice record levels dont have to match exactly. diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 87a3220232..089fba6566 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -430,10 +430,10 @@ bool BackgroundSlicingProcess::empty() const return m_print->empty(); } -std::string BackgroundSlicingProcess::validate() +std::string BackgroundSlicingProcess::validate(std::string* warning) { assert(m_print != nullptr); - return m_print->validate(); + return m_print->validate(warning); } // Apply config over the print. Returns false, if the new config values caused any of the already diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index b3f8a0a6bb..d3819f15c6 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -131,7 +131,7 @@ public: bool empty() const; // Validate the print. Returns an empty string if valid, returns an error message if invalid. // Call validate before calling start(). - std::string validate(); + std::string validate(std::string* warning = nullptr); // Set the export path of the G-code. // Once the path is set, the G-code diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 31dace42e5..9ce278792c 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -71,7 +71,9 @@ enum class NotificationType // Notification that custom supports/seams were deleted after mesh repair. CustomSupportsAndSeamRemovedAfterRepair, // Notification that auto adding of color changes is impossible - EmptyAutoColorChange + EmptyAutoColorChange, + // Notification emitted by Print::validate + PrintValidateWarning }; class NotificationManager diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 762acfdf1a..93d0e08819 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1599,6 +1599,8 @@ struct Plater::priv void suppress_snapshots() { this->m_prevent_snapshots++; } void allow_snapshots() { this->m_prevent_snapshots--; } + void process_validation_warning(const std::string& warning) const; + bool background_processing_enabled() const { return this->get_config("background_processing") == "1"; } void update_print_volume_state(); void schedule_background_process(); @@ -2787,6 +2789,41 @@ void Plater::priv::update_print_volume_state() this->q->model().update_print_volume_state(print_volume); } + +void Plater::priv::process_validation_warning(const std::string& warning) const +{ + if (warning.empty()) + notification_manager->close_notification_of_type(NotificationType::PrintValidateWarning); + else { + std::string text = warning; + std::string hypertext = ""; + std::function action_fn = [](wxEvtHandler*){ return false; }; + + if (text == "_SUPPORTS_OFF") { + text = _u8L("An object has custom support enforcers which will not be used " + "because supports are disabled.")+"\n"; + hypertext = _u8L("Enable supports for enforcers only"); + action_fn = [](wxEvtHandler*) { + Tab* print_tab = wxGetApp().get_tab(Preset::TYPE_PRINT); + assert(print_tab); + DynamicPrintConfig& config = wxGetApp().preset_bundle->prints.get_edited_preset().config; + config.set_key_value("support_material", new ConfigOptionBool(true)); + config.set_key_value("support_material_auto", new ConfigOptionBool(false)); + print_tab->on_value_change("support_material", config.opt_bool("support_material")); + print_tab->on_value_change("support_material_auto", config.opt_bool("support_material_auto")); + return true; + }; + } + + notification_manager->push_notification( + NotificationType::PrintValidateWarning, + NotificationManager::NotificationLevel::ImportantNotification, + text, hypertext, action_fn + ); + } +} + + // Update background processing thread from the current config and Model. // Returns a bitmask of UpdateBackgroundProcessReturnState. unsigned int Plater::priv::update_background_process(bool force_validation, bool postpone_error_messages) @@ -2829,17 +2866,23 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool // The delayed error message is no more valid. this->delayed_error_message.clear(); // The state of the Print changed, and it is non-zero. Let's validate it and give the user feedback on errors. - std::string err = this->background_process.validate(); + std::string warning; + std::string err = this->background_process.validate(&warning); if (err.empty()) { notification_manager->set_all_slicing_errors_gray(true); if (invalidated != Print::APPLY_STATUS_UNCHANGED && this->background_processing_enabled()) return_state |= UPDATE_BACKGROUND_PROCESS_RESTART; + + // Pass a warning from validation and either show a notification, + // or hide the old one. + process_validation_warning(warning); } else { // The print is not valid. // Show error as notification. notification_manager->push_slicing_error_notification(err); return_state |= UPDATE_BACKGROUND_PROCESS_INVALID; } + } else if (! this->delayed_error_message.empty()) { // Reusing the old state. return_state |= UPDATE_BACKGROUND_PROCESS_INVALID; From 70573484c29fc7f80a634a4eedbbb9c1d495c23e Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 2 Mar 2021 11:47:09 +0100 Subject: [PATCH 049/285] Show an error notification when attempting to save 3MF while editing SLA support points The notification disappears when it is no longer valid. --- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 2 ++ src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 8 +++++--- src/slic3r/GUI/NotificationManager.hpp | 4 +++- src/slic3r/GUI/Plater.cpp | 4 +++- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index b5dbab2844..b200623f49 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -16,6 +16,7 @@ #include "slic3r/GUI/GUI_ObjectSettings.hpp" #include "slic3r/GUI/GUI_ObjectList.hpp" #include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/NotificationManager.hpp" #include "libslic3r/PresetBundle.hpp" #include "libslic3r/SLAPrint.hpp" @@ -1161,6 +1162,7 @@ void GLGizmoSlaSupports::disable_editing_mode() m_c->instances_hider()->show_supports(true); m_parent.set_as_dirty(); } + wxGetApp().plater()->get_notification_manager()->close_notification_of_type(NotificationType::QuitSLAManualMode); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index abbc8599bd..9dc785b3fa 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -1147,9 +1147,11 @@ bool GLGizmosManager::is_in_editing_mode(bool error_notification) const return false; if (error_notification) - wxGetApp().plater()->get_notification_manager()->push_slicing_error_notification( - _u8L("You are currently editing SLA support points. Please, apply or discard " - "your changes first.")); + wxGetApp().plater()->get_notification_manager()->push_notification( + NotificationType::QuitSLAManualMode, + NotificationManager::NotificationLevel::ErrorNotification, + _u8L("You are currently editing SLA support points. Please, " + "apply or discard your changes first.")); return true; } diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 9ce278792c..62c4ea845e 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -73,7 +73,9 @@ enum class NotificationType // Notification that auto adding of color changes is impossible EmptyAutoColorChange, // Notification emitted by Print::validate - PrintValidateWarning + PrintValidateWarning, + // Notification telling user to quit SLA supports manual editing + QuitSLAManualMode }; class NotificationManager diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 93d0e08819..15b3a17c73 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5418,7 +5418,9 @@ void Plater::export_amf() void Plater::export_3mf(const boost::filesystem::path& output_path) { - if (p->model.objects.empty()) { return; } + if (p->model.objects.empty() + || canvas3D()->get_gizmos_manager().is_in_editing_mode(true)) + return; wxString path; bool export_config = true; From cee4ed9ff24f2ea09c4ab9bf87d6e1dcebbc87c2 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 4 Mar 2021 13:23:24 +0100 Subject: [PATCH 050/285] G-code window optimization: parse g-code lines only when needed --- src/slic3r/GUI/GCodeViewer.cpp | 88 +++++++++++++++++++++------------- src/slic3r/GUI/GCodeViewer.hpp | 9 ++++ 2 files changed, 64 insertions(+), 33 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 6d59866909..3cbf3262f1 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -314,6 +314,8 @@ void GCodeViewer::SequentialView::GCodeWindow::load_gcode() } m_file_size = static_cast(m_gcode.size()); + m_last_line_id = 0; + m_last_lines_size = 0; } void GCodeViewer::SequentialView::GCodeWindow::start_mapping_file() @@ -328,6 +330,38 @@ void GCodeViewer::SequentialView::GCodeWindow::stop_mapping_file() void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, unsigned int curr_line_id) const { + auto update_lines = [this](unsigned int start_id, unsigned int end_id) { + std::vector ret; + ret.reserve(end_id - start_id + 1); + for (unsigned int id = start_id; id <= end_id; ++id) { + const std::string& gline = m_gcode[id - 1]; + + std::string command; + std::string parameters; + std::string comment; + + // extract comment + std::vector tokens; + boost::split(tokens, gline, boost::is_any_of(";"), boost::token_compress_on); + command = tokens.front(); + if (tokens.size() > 1) + comment = ";" + tokens.back(); + + // extract gcode command and parameters + if (!command.empty()) { + boost::split(tokens, command, boost::is_any_of(" "), boost::token_compress_on); + command = tokens.front(); + if (tokens.size() > 1) { + for (size_t i = 1; i < tokens.size(); ++i) { + parameters += " " + tokens[i]; + } + } + } + ret.push_back({ command, parameters, comment }); + } + return ret; + }; + static const ImVec4 LINE_NUMBER_COLOR = ImGuiWrapper::COL_ORANGE_LIGHT; static const ImVec4 HIGHLIGHT_RECT_COLOR = ImGuiWrapper::COL_ORANGE_DARK; static const ImVec4 COMMAND_COLOR = { 0.8f, 0.8f, 0.0f, 1.0f }; @@ -340,12 +374,13 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u const float text_height = ImGui::CalcTextSize("0").y; const ImGuiStyle& style = ImGui::GetStyle(); const float wnd_height = bottom - top; - if (wnd_height < 2.0f * (text_height + style.ItemSpacing.y + style.WindowPadding.y)) - return; // number of visible lines const unsigned int lines_count = (wnd_height - 2.0f * style.WindowPadding.y + style.ItemSpacing.y) / (text_height + style.ItemSpacing.y); + if (lines_count == 0) + return; + // visible range const unsigned int half_lines_count = lines_count / 2; unsigned int start_id = (curr_line_id >= half_lines_count) ? curr_line_id - half_lines_count : 0; @@ -355,6 +390,13 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u start_id = end_id - lines_count + 1; } + if (m_last_line_id != curr_line_id || m_last_lines_size != static_cast(end_id - start_id + 1)) { + // updates list of lines to show + *const_cast*>(&m_lines) = update_lines(start_id, end_id); + *const_cast(&m_last_line_id) = curr_line_id; + *const_cast(&m_last_lines_size) = m_lines.size(); + } + // line id number column width const float id_width = ImGui::CalcTextSize(std::to_string(end_id).c_str()).x; @@ -368,33 +410,13 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u ImDrawList* draw_list = ImGui::GetWindowDrawList(); + // center the text in the window by pushing down the first line const float f_lines_count = static_cast(lines_count); ImGui::SetCursorPosY({ 0.5f * (wnd_height - f_lines_count * text_height - (f_lines_count - 1.0f) * style.ItemSpacing.y) }); + // render text lines for (unsigned int id = start_id; id <= end_id; ++id) { - const std::string& line = m_gcode[id - 1]; - - std::string command; - std::string parameters; - std::string comment; - - // extract comment - std::vector tokens; - boost::split(tokens, line, boost::is_any_of(";"), boost::token_compress_on); - command = tokens.front(); - if (tokens.size() > 1) - comment = ";" + tokens.back(); - - // extract gcode command and parameters - if (!command.empty()) { - boost::split(tokens, command, boost::is_any_of(" "), boost::token_compress_on); - command = tokens.front(); - if (tokens.size() > 1) { - for (size_t i = 1; i < tokens.size(); ++i) { - parameters += " " + tokens[i]; - } - } - } + const Line& line = m_lines[id - start_id]; // rect for the current selected move if (id == curr_line_id) { @@ -414,28 +436,28 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u imgui.text(id_str); ImGui::PopStyleColor(); - if (!command.empty() || !comment.empty()) + if (!line.command.empty() || !line.comment.empty()) ImGui::SameLine(); // render command - if (!command.empty()) { + if (!line.command.empty()) { ImGui::PushStyleColor(ImGuiCol_Text, COMMAND_COLOR); - imgui.text(command); + imgui.text(line.command); ImGui::PopStyleColor(); } // render command parameters - if (!parameters.empty()) { + if (!line.parameters.empty()) { ImGui::SameLine(0.0f, 0.0f); - imgui.text(parameters); + imgui.text(line.parameters); } // render comment - if (!comment.empty()) { - if (!command.empty()) + if (!line.comment.empty()) { + if (!line.command.empty()) ImGui::SameLine(0.0f, 0.0f); ImGui::PushStyleColor(ImGuiCol_Text, COMMENT_COLOR); - imgui.text(comment); + imgui.text(line.comment); ImGui::PopStyleColor(); } } diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index ada377d364..428a50e851 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -609,8 +609,17 @@ public: #if ENABLE_GCODE_WINDOW class GCodeWindow { + struct Line + { + std::string command; + std::string parameters; + std::string comment; + }; bool m_visible{ true }; unsigned int m_file_size{ 0 }; + unsigned int m_last_line_id{ 0 }; + size_t m_last_lines_size{ 0 }; + std::vector m_lines; std::string m_filename; std::vector m_gcode; From b748f36246064cd86f005e81df05c83994495b4e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 4 Mar 2021 13:36:07 +0100 Subject: [PATCH 051/285] Fixed layout of loading file progress dialog --- src/slic3r/GUI/Plater.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 15b3a17c73..3a227bfb32 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2134,7 +2134,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ const auto loading = _L("Loading") + dots; wxProgressDialog dlg(loading, "", 100, q, wxPD_AUTO_HIDE); - dlg.Pulse(); + wxBusyCursor busy; auto *new_model = (!load_model || one_by_one) ? nullptr : new Slic3r::Model(); std::vector obj_idxs; @@ -2144,6 +2144,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ const auto filename = path.filename(); const auto dlg_info = _L("Loading file") + ": " + from_path(filename); dlg.Update(static_cast(100.0f * static_cast(i) / static_cast(input_files.size())), dlg_info); + dlg.Fit(); const bool type_3mf = std::regex_match(path.string(), pattern_3mf); const bool type_zip_amf = !type_3mf && std::regex_match(path.string(), pattern_zip_amf); From b6634b2ea0474e06303b03a39648a51cb40ace3c Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 4 Mar 2021 15:09:50 +0100 Subject: [PATCH 052/285] Added progress dialog for adding parts from files --- src/slic3r/GUI/GUI_ObjectList.cpp | 16 +++++++++++----- src/slic3r/GUI/Plater.cpp | 30 ++++++++++-------------------- 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index ad5ee151d8..54e0a2b4f1 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2133,12 +2133,11 @@ void ObjectList::load_subobject(ModelVolumeType type) if (m_objects_model->GetItemType(item)&itInstance) item = m_objects_model->GetItemById(obj_idx); - take_snapshot(_(L("Load Part"))); + take_snapshot(_L("Load Part")); std::vector> volumes_info; load_part((*m_objects)[obj_idx], volumes_info, type); - changed_object(obj_idx); if (type == ModelVolumeType::MODEL_PART) // update printable state on canvas @@ -2164,23 +2163,30 @@ void ObjectList::load_part( ModelObject* model_object, wxArrayString input_files; wxGetApp().import_model(parent, input_files); + + wxProgressDialog dlg(_L("Loading") + dots, "", 100, wxGetApp().plater(), wxPD_AUTO_HIDE); + wxBusyCursor busy; + for (size_t i = 0; i < input_files.size(); ++i) { std::string input_file = input_files.Item(i).ToUTF8().data(); + dlg.Update(static_cast(100.0f * static_cast(i) / static_cast(input_files.size())), + _L("Loading file") + ": " + from_path(boost::filesystem::path(input_file).filename())); + dlg.Fit(); + Model model; try { model = Model::read_from_file(input_file); } catch (std::exception &e) { - auto msg = _(L("Error!")) + " " + input_file + " : " + e.what() + "."; + auto msg = _L("Error!") + " " + input_file + " : " + e.what() + "."; show_error(parent, msg); exit(1); } for (auto object : model.objects) { Vec3d delta = Vec3d::Zero(); - if (model_object->origin_translation != Vec3d::Zero()) - { + if (model_object->origin_translation != Vec3d::Zero()) { object->center_around_origin(); delta = model_object->origin_translation - object->origin_translation; } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 3a227bfb32..e6573d1b82 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2142,8 +2142,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ for (size_t i = 0; i < input_files.size(); ++i) { const auto &path = input_files[i]; const auto filename = path.filename(); - const auto dlg_info = _L("Loading file") + ": " + from_path(filename); - dlg.Update(static_cast(100.0f * static_cast(i) / static_cast(input_files.size())), dlg_info); + dlg.Update(static_cast(100.0f * static_cast(i) / static_cast(input_files.size())), _L("Loading file") + ": " + from_path(filename)); dlg.Fit(); const bool type_3mf = std::regex_match(path.string(), pattern_3mf); @@ -2164,12 +2163,10 @@ std::vector Plater::priv::load_files(const std::vector& input_ PrinterTechnology printer_technology = Preset::printer_technology(config_loaded); // We can't to load SLA project if there is at least one multi-part object on the bed - if (printer_technology == ptSLA) - { + if (printer_technology == ptSLA) { const ModelObjectPtrs& objects = q->model().objects; for (auto object : objects) - if (object->volumes.size() > 1) - { + if (object->volumes.size() > 1) { Slic3r::GUI::show_info(nullptr, _L("You cannot load SLA project with a multi-part object on the bed") + "\n\n" + _L("Please check your object list before preset changing."), @@ -2188,8 +2185,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ this->model.custom_gcode_per_print_z = model.custom_gcode_per_print_z; } - if (load_config) - { + if (load_config) { if (!config.empty()) { Preset::normalize(config); PresetBundle* preset_bundle = wxGetApp().preset_bundle; @@ -2264,8 +2260,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ continue; } - if (load_model) - { + if (load_model) { // The model should now be initialized auto convert_from_imperial_units = [](Model& model, bool only_small_volumes) { @@ -2315,8 +2310,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ else if ((wxGetApp().get_mode() == comSimple) && (type_3mf || type_any_amf) && model_has_advanced_features(model)) { wxMessageDialog msg_dlg(q, _L("This file cannot be loaded in a simple mode. Do you want to switch to an advanced mode?")+"\n", _L("Detected advanced data"), wxICON_WARNING | wxYES | wxNO); - if (msg_dlg.ShowModal() == wxID_YES) - { + if (msg_dlg.ShowModal() == wxID_YES) { Slic3r::GUI::wxGetApp().save_mode(comAdvanced); view3D->set_as_dirty(); } @@ -2331,8 +2325,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ } // check multi-part object adding for the SLA-printing - if (printer_technology == ptSLA) - { + if (printer_technology == ptSLA) { for (auto obj : model.objects) if ( obj->volumes.size()>1 ) { Slic3r::GUI::show_error(nullptr, @@ -2368,23 +2361,20 @@ std::vector Plater::priv::load_files(const std::vector& input_ obj_idxs.insert(obj_idxs.end(), loaded_idxs.begin(), loaded_idxs.end()); } - if (load_model) - { + if (load_model) { wxGetApp().app_config->update_skein_dir(input_files[input_files.size() - 1].parent_path().string()); // XXX: Plater.pm had @loaded_files, but didn't seem to fill them with the filenames... statusbar()->set_status_text(_L("Loaded")); } // automatic selection of added objects - if (!obj_idxs.empty() && (view3D != nullptr)) - { + if (!obj_idxs.empty() && view3D != nullptr) { // update printable state for new volumes on canvas3D wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_objects(obj_idxs); Selection& selection = view3D->get_canvas3d()->get_selection(); selection.clear(); - for (size_t idx : obj_idxs) - { + for (size_t idx : obj_idxs) { selection.add_object((unsigned int)idx, false); } From c8fdd837aead81c4aa9a429d9d5f2e3b3fe9366b Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 5 Mar 2021 11:27:25 +0100 Subject: [PATCH 053/285] Added a missing include for gcc --- src/slic3r/GUI/GUI_ObjectList.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 54e0a2b4f1..57f1841e3b 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -16,6 +16,8 @@ #include "format.hpp" #include +#include + #include "slic3r/Utils/FixModelByWin10.hpp" #ifdef __WXMSW__ From c25c435d523d377ef7bce1b70f9f4bd62eb1b7ba Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 5 Mar 2021 13:54:29 +0100 Subject: [PATCH 054/285] Added a new placeholder for custom toolchange gcode (toolchange_z) This always contains the actual print_z of the toolchange, while layer_z contains the print_z of the print. The two differ in case that wipe tower without sparse layers is used. Related to #3834. --- src/libslic3r/GCode.cpp | 2 ++ src/libslic3r/PrintConfig.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index ca714549db..5120357985 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -285,6 +285,7 @@ namespace Slic3r { config.set_key_value("next_extruder", new ConfigOptionInt((int)new_extruder_id)); config.set_key_value("layer_num", new ConfigOptionInt(gcodegen.m_layer_index)); config.set_key_value("layer_z", new ConfigOptionFloat(tcr.print_z)); + config.set_key_value("toolchange_z", new ConfigOptionFloat(z)); // config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z)); toolchange_gcode_str = gcodegen.placeholder_parser_process("toolchange_gcode", toolchange_gcode, new_extruder_id, &config); check_add_eol(toolchange_gcode_str); @@ -3003,6 +3004,7 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) config.set_key_value("next_extruder", new ConfigOptionInt((int)extruder_id)); config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); + config.set_key_value("toolchange_z", new ConfigOptionFloat(print_z)); config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z)); toolchange_gcode_parsed = placeholder_parser_process("toolchange_gcode", toolchange_gcode, extruder_id, &config); gcode += toolchange_gcode_parsed; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 810b651c40..1b44bdf41f 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2438,7 +2438,7 @@ void PrintConfigDef::init_fff_params() def = this->add("toolchange_gcode", coString); def->label = L("Tool change G-code"); def->tooltip = L("This custom code is inserted before every toolchange. Placeholder variables for all PrusaSlicer settings " - "as well as {previous_extruder} and {next_extruder} can be used. When a tool-changing command " + "as well as {toolchange_z}, {previous_extruder} and {next_extruder} can be used. When a tool-changing command " "which changes to the correct extruder is included (such as T{next_extruder}), PrusaSlicer will emit no other such command. " "It is therefore possible to script custom behaviour both before and after the toolchange."); def->multiline = true; From 1efa9a06f67d7796bda21fedcff7bdabadbd66a5 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 4 Mar 2021 11:18:50 +0100 Subject: [PATCH 055/285] Adjustable wipe tower brim --- src/libslic3r/GCode/WipeTower.cpp | 25 +++++++++++++++---------- src/libslic3r/GCode/WipeTower.hpp | 5 +++-- src/libslic3r/Preset.cpp | 2 +- src/libslic3r/Print.cpp | 6 +++--- src/libslic3r/Print.hpp | 2 +- src/libslic3r/PrintConfig.cpp | 10 +++++++++- src/libslic3r/PrintConfig.hpp | 2 ++ src/slic3r/GUI/3DScene.cpp | 27 +++++++++++++++++---------- src/slic3r/GUI/ConfigManipulation.cpp | 2 +- src/slic3r/GUI/GCodeViewer.cpp | 8 ++------ src/slic3r/GUI/GLCanvas3D.cpp | 8 ++------ src/slic3r/GUI/Plater.cpp | 2 +- src/slic3r/GUI/Tab.cpp | 1 + 13 files changed, 58 insertions(+), 42 deletions(-) diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index 24248e16c1..f3ecd2cf33 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -520,6 +520,7 @@ WipeTower::WipeTower(const PrintConfig& config, const std::vector> &result); float get_depth() const { return m_wipe_tower_depth; } - float get_brim_width() const { return m_wipe_tower_brim_width; } + float get_brim_width() const { return m_wipe_tower_brim_width_real; } @@ -220,7 +220,8 @@ private: Vec2f m_wipe_tower_pos; // Left front corner of the wipe tower in mm. float m_wipe_tower_width; // Width of the wipe tower. float m_wipe_tower_depth = 0.f; // Depth of the wipe tower - float m_wipe_tower_brim_width = 0.f; // Width of brim (mm) + float m_wipe_tower_brim_width = 0.f; // Width of brim (mm) from config + float m_wipe_tower_brim_width_real = 0.f; // Width of brim (mm) after generation float m_wipe_tower_rotation_angle = 0.f; // Wipe tower rotation angle in degrees (with respect to x axis) float m_internal_rotation = 0.f; float m_y_shift = 0.f; // y shift passed to writer diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 98ccf280f2..67b1ebd4fb 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -437,7 +437,7 @@ const std::vector& Preset::print_options() "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", "top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "infill_anchor", "infill_anchor_max", "bridge_flow_ratio", "clip_multipart_objects", "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y", - "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", "single_extruder_multi_material_priming", + "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits" }; return s_opts; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 89a8dc77b3..c5babb248b 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -202,6 +202,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n || opt_key == "temperature" || opt_key == "wipe_tower" || opt_key == "wipe_tower_width" + || opt_key == "wipe_tower_brim_width" || opt_key == "wipe_tower_bridging" || opt_key == "wipe_tower_no_sparse_layers" || opt_key == "wiping_volumes_matrix" @@ -1909,16 +1910,15 @@ bool Print::has_wipe_tower() const m_config.nozzle_diameter.values.size() > 1; } -const WipeTowerData& Print::wipe_tower_data(size_t extruders_cnt, double first_layer_height, double nozzle_diameter) const +const WipeTowerData& Print::wipe_tower_data(size_t extruders_cnt) const { // If the wipe tower wasn't created yet, make sure the depth and brim_width members are set to default. if (! is_step_done(psWipeTower) && extruders_cnt !=0) { float width = float(m_config.wipe_tower_width); - float brim_spacing = float(nozzle_diameter * 1.25f - first_layer_height * (1. - M_PI_4)); const_cast(this)->m_wipe_tower_data.depth = (900.f/width) * float(extruders_cnt - 1); - const_cast(this)->m_wipe_tower_data.brim_width = 4.5f * brim_spacing; + const_cast(this)->m_wipe_tower_data.brim_width = m_config.wipe_tower_brim_width; } return m_wipe_tower_data; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 9f22ddf133..7311bdc795 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -493,7 +493,7 @@ public: // Wipe tower support. bool has_wipe_tower() const; - const WipeTowerData& wipe_tower_data(size_t extruders_cnt = 0, double first_layer_height = 0., double nozzle_diameter = 0.) const; + const WipeTowerData& wipe_tower_data(size_t extruders_cnt = 0) const; const ToolOrdering& tool_ordering() const { return m_tool_ordering; } std::string output_filename(const std::string &filename_base = std::string()) const override; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 1b44bdf41f..e534f140a5 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2594,10 +2594,18 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloat(0.)); + def = this->add("wipe_tower_brim_width", coFloat); + def->label = L("Wipe tower brim width"); + def->tooltip = L("Wipe tower brim width"); + def->sidetext = L("mm"); + def->mode = comAdvanced; + def->min = 0.f; + def->set_default_value(new ConfigOptionFloat(2.)); + def = this->add("wipe_into_infill", coBool); def->category = L("Wipe options"); def->label = L("Wipe into this object's infill"); - def->tooltip = L("Purging after toolchange will done inside this object's infills. " + def->tooltip = L("Purging after toolchange will be done inside this object's infills. " "This lowers the amount of waste but may result in longer print time " " due to additional travel moves."); def->set_default_value(new ConfigOptionBool(false)); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index c1e3f82cbe..76085c9417 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -962,6 +962,7 @@ public: ConfigOptionFloat wipe_tower_width; ConfigOptionFloat wipe_tower_per_color_wipe; ConfigOptionFloat wipe_tower_rotation_angle; + ConfigOptionFloat wipe_tower_brim_width; ConfigOptionFloat wipe_tower_bridging; ConfigOptionFloats wiping_volumes_matrix; ConfigOptionFloats wiping_volumes_extruders; @@ -1036,6 +1037,7 @@ protected: OPT_PTR(wipe_tower_width); OPT_PTR(wipe_tower_per_color_wipe); OPT_PTR(wipe_tower_rotation_angle); + OPT_PTR(wipe_tower_brim_width); OPT_PTR(wipe_tower_bridging); OPT_PTR(wiping_volumes_matrix); OPT_PTR(wiping_volumes_extruders); diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 04301061db..4cab8f3db9 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -610,25 +610,28 @@ void GLVolumeCollection::load_object_auxiliary( } int GLVolumeCollection::load_wipe_tower_preview( - int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width, bool opengl_initialized) + int obj_idx, float pos_x, float pos_y, float width, float depth, float height, + float rotation_angle, bool size_unknown, float brim_width, bool opengl_initialized) { if (depth < 0.01f) return int(this->volumes.size() - 1); if (height == 0.0f) height = 0.1f; - Point origin_of_rotation(0.f, 0.f); + TriangleMesh mesh; float color[4] = { 0.5f, 0.5f, 0.0f, 1.f }; - // In case we don't know precise dimensions of the wipe tower yet, we'll draw the box with different color with one side jagged: + // In case we don't know precise dimensions of the wipe tower yet, we'll draw + // the box with different color with one side jagged: if (size_unknown) { color[0] = 0.9f; color[1] = 0.6f; - depth = std::max(depth, 10.f); // Too narrow tower would interfere with the teeth. The estimate is not precise anyway. + // Too narrow tower would interfere with the teeth. The estimate is not precise anyway. + depth = std::max(depth, 10.f); float min_width = 30.f; - // We'll now create the box with jagged edge. y-coordinates of the pre-generated model are shifted so that the front - // edge has y=0 and centerline of the back edge has y=depth: + // We'll now create the box with jagged edge. y-coordinates of the pre-generated model + // are shifted so that the front edge has y=0 and centerline of the back edge has y=depth: Pointf3s points; std::vector facets; float out_points_idx[][3] = { { 0, -depth, 0 }, { 0, 0, 0 }, { 38.453f, 0, 0 }, { 61.547f, 0, 0 }, { 100.0f, 0, 0 }, { 100.0f, -depth, 0 }, { 55.7735f, -10.0f, 0 }, { 44.2265f, 10.0f, 0 }, @@ -637,13 +640,17 @@ int GLVolumeCollection::load_wipe_tower_preview( {8, 10, 14}, {3, 12, 4}, {3, 13, 12}, {6, 13, 3}, {6, 14, 13}, {7, 14, 6}, {7, 15, 14}, {2, 15, 7}, {2, 8, 15}, {1, 8, 2}, {1, 9, 8}, {0, 9, 1}, {0, 10, 9}, {5, 10, 0}, {5, 11, 10}, {4, 11, 5}, {4, 12, 11} }; for (int i = 0; i < 16; ++i) - points.emplace_back(out_points_idx[i][0] / (100.f / min_width), out_points_idx[i][1] + depth, out_points_idx[i][2]); + points.emplace_back(out_points_idx[i][0] / (100.f / min_width), + out_points_idx[i][1] + depth, out_points_idx[i][2]); for (int i = 0; i < 28; ++i) - facets.emplace_back(out_facets_idx[i][0], out_facets_idx[i][1], out_facets_idx[i][2]); + facets.emplace_back(out_facets_idx[i][0], + out_facets_idx[i][1], + out_facets_idx[i][2]); TriangleMesh tooth_mesh(points, facets); - // We have the mesh ready. It has one tooth and width of min_width. We will now append several of these together until we are close to - // the required width of the block. Than we can scale it precisely. + // We have the mesh ready. It has one tooth and width of min_width. We will now + // append several of these together until we are close to the required width + // of the block. Than we can scale it precisely. size_t n = std::max(1, int(width / min_width)); // How many shall be merged? for (size_t i = 0; i < n; ++i) { mesh.merge(tooth_mesh); diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index b7cff7c7fc..01ec72837a 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -310,7 +310,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) toggle_field("standby_temperature_delta", have_ooze_prevention); bool have_wipe_tower = config->opt_bool("wipe_tower"); - for (auto el : { "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", + for (auto el : { "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "wipe_tower_no_sparse_layers", "single_extruder_multi_material_priming" }) toggle_field(el, have_wipe_tower); diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index e1c61bbd34..66762eaeef 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -2681,12 +2681,8 @@ void GCodeViewer::load_shells(const Print& print, bool initialized) const PrintConfig& config = print.config(); size_t extruders_count = config.nozzle_diameter.size(); if ((extruders_count > 1) && config.wipe_tower && !config.complete_objects) { - const DynamicPrintConfig& print_config = wxGetApp().preset_bundle->prints.get_edited_preset().config; - double layer_height = print_config.opt_float("layer_height"); - double first_layer_height = print_config.get_abs_value("first_layer_height", layer_height); - double nozzle_diameter = print.config().nozzle_diameter.values[0]; - float depth = print.wipe_tower_data(extruders_count, first_layer_height, nozzle_diameter).depth; - float brim_width = print.wipe_tower_data(extruders_count, first_layer_height, nozzle_diameter).brim_width; + float depth = print.wipe_tower_data(extruders_count).depth; + float brim_width = print.wipe_tower_data(extruders_count).brim_width; m_shells.volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle, !print.is_step_done(psWipeTower), brim_width, initialized); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 3267320df2..a45f61cabd 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2211,12 +2211,8 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re const Print *print = m_process->fff_print(); - const DynamicPrintConfig &print_config = wxGetApp().preset_bundle->prints.get_edited_preset().config; - double layer_height = print_config.opt_float("layer_height"); - double first_layer_height = print_config.get_abs_value("first_layer_height", layer_height); - double nozzle_diameter = print->config().nozzle_diameter.values[0]; - float depth = print->wipe_tower_data(extruders_count, first_layer_height, nozzle_diameter).depth; - float brim_width = print->wipe_tower_data(extruders_count, first_layer_height, nozzle_diameter).brim_width; + float depth = print->wipe_tower_data(extruders_count).depth; + float brim_width = print->wipe_tower_data(extruders_count).brim_width; int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview( 1000, x, y, w, depth, (float)height, a, !print->is_step_done(psWipeTower), diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index e6573d1b82..7a38dd3a1c 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1757,7 +1757,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) , config(Slic3r::DynamicPrintConfig::new_from_defaults_keys({ "bed_shape", "bed_custom_texture", "bed_custom_model", "complete_objects", "duplicate_distance", "extruder_clearance_radius", "skirts", "skirt_distance", "brim_width", "brim_offset", "brim_type", "variable_layer_height", "nozzle_diameter", "single_extruder_multi_material", - "wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", + "wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "extruder_colour", "filament_colour", "max_print_height", "printer_model", "printer_technology", // These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor. "layer_height", "first_layer_height", "min_layer_height", "max_layer_height", diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index eeb420e3e0..d737bae101 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1572,6 +1572,7 @@ void TabPrint::build() optgroup->append_single_option_line("wipe_tower_y"); optgroup->append_single_option_line("wipe_tower_width"); optgroup->append_single_option_line("wipe_tower_rotation_angle"); + optgroup->append_single_option_line("wipe_tower_brim_width"); optgroup->append_single_option_line("wipe_tower_bridging"); optgroup->append_single_option_line("wipe_tower_no_sparse_layers"); optgroup->append_single_option_line("single_extruder_multi_material_priming"); From 0472c8452588809b382681ef1e418242f41b47e0 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 8 Mar 2021 08:35:32 +0100 Subject: [PATCH 056/285] G-code window uses file mapping --- CMakeLists.txt | 2 +- src/libslic3r/Technologies.hpp | 3 + src/slic3r/GUI/GCodeViewer.cpp | 140 +++++++++++++++++++++++++-------- src/slic3r/GUI/GCodeViewer.hpp | 40 +++++++++- src/slic3r/GUI/GLCanvas3D.cpp | 4 + src/slic3r/GUI/GLCanvas3D.hpp | 2 + src/slic3r/GUI/Plater.cpp | 4 + 7 files changed, 159 insertions(+), 36 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 98ae4f0c68..5ebda365d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -300,7 +300,7 @@ endif() # boost::process was introduced first in version 1.64.0, # boost::beast::detail::base64 was introduced first in version 1.66.0 set(MINIMUM_BOOST_VERSION "1.66.0") -set(_boost_components "system;filesystem;thread;log;locale;regex;chrono;atomic;date_time") +set(_boost_components "system;filesystem;thread;log;locale;regex;chrono;atomic;date_time;iostreams") find_package(Boost ${MINIMUM_BOOST_VERSION} REQUIRED COMPONENTS ${_boost_components}) add_library(boost_libs INTERFACE) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index c2139bb33a..a4c6aa214a 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -53,7 +53,10 @@ #define ENABLE_GCODE_LINES_ID_IN_H_SLIDER (1 && ENABLE_2_3_1_ALPHA1) // Enable validation of custom gcode against gcode processor reserved keywords #define ENABLE_VALIDATE_CUSTOM_GCODE (1 && ENABLE_2_3_1_ALPHA1) +// Enable showing a imgui window containing gcode in preview #define ENABLE_GCODE_WINDOW (1 && ENABLE_2_3_1_ALPHA1) +// Enable using file mapping to show a imgui window containing gcode in preview +#define ENABLE_GCODE_WINDOW_USE_MAPPED_FILE (1 && ENABLE_GCODE_WINDOW) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 3cbf3262f1..c6010ae423 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -303,21 +303,57 @@ void GCodeViewer::SequentialView::Marker::render() const #if ENABLE_GCODE_WINDOW void GCodeViewer::SequentialView::GCodeWindow::load_gcode() { +#if !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE m_gcode.clear(); +#endif // !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE if (m_filename.empty()) return; - boost::nowide::ifstream f(m_filename); - std::string line; - while (std::getline(f, line)) { - m_gcode.push_back(line); + try + { + boost::nowide::ifstream f(m_filename); + std::string line; +#if ENABLE_GCODE_WINDOW_USE_MAPPED_FILE + // generate mapping for accessing data in file by line number + uint64_t offset = 0; +#endif // ENABLE_GCODE_WINDOW_USE_MAPPED_FILE + while (std::getline(f, line)) { +#if ENABLE_GCODE_WINDOW_USE_MAPPED_FILE + size_t line_length = static_cast(line.length()); + m_lines_map.push_back({ offset, line_length }); + offset += static_cast(line_length) + 1; +#else + m_gcode.push_back(line); +#endif // ENABLE_GCODE_WINDOW_USE_MAPPED_FILE + } + } + catch (...) + { + BOOST_LOG_TRIVIAL(error) << "Unable to load data from " << m_filename << ". Cannot show G-code window."; + reset(); + return; } - m_file_size = static_cast(m_gcode.size()); - m_last_line_id = 0; + m_selected_line_id = 0; m_last_lines_size = 0; + +#if ENABLE_GCODE_WINDOW_USE_MAPPED_FILE + try + { + m_file.open(boost::filesystem::path(m_filename)); + if (m_file.is_open()) { + std::cout << "open file: " << m_filename << "\n"; + } + } + catch (...) + { + BOOST_LOG_TRIVIAL(error) << "Unable to map file " << m_filename << ". Cannot show G-code window."; + reset(); + } +#endif // ENABLE_GCODE_WINDOW_USE_MAPPED_FILE } +#if !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE void GCodeViewer::SequentialView::GCodeWindow::start_mapping_file() { std::cout << "GCodeViewer::SequentialView::GCodeWindow::start_mapping_file()\n"; @@ -327,14 +363,20 @@ void GCodeViewer::SequentialView::GCodeWindow::stop_mapping_file() { std::cout << "GCodeViewer::SequentialView::GCodeWindow::stop_mapping_file()\n"; } +#endif // !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE -void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, unsigned int curr_line_id) const +void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, uint64_t curr_line_id) const { - auto update_lines = [this](unsigned int start_id, unsigned int end_id) { + auto update_lines = [this](uint64_t start_id, uint64_t end_id) { std::vector ret; ret.reserve(end_id - start_id + 1); - for (unsigned int id = start_id; id <= end_id; ++id) { + for (uint64_t id = start_id; id <= end_id; ++id) { +#if ENABLE_GCODE_WINDOW_USE_MAPPED_FILE + // read line from file + std::string gline(m_file.data() + m_lines_map[id - 1].first, m_lines_map[id - 1].second); +#else const std::string& gline = m_gcode[id - 1]; +#endif // ENABLE_GCODE_WINDOW_USE_MAPPED_FILE std::string command; std::string parameters; @@ -363,41 +405,66 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u }; static const ImVec4 LINE_NUMBER_COLOR = ImGuiWrapper::COL_ORANGE_LIGHT; - static const ImVec4 HIGHLIGHT_RECT_COLOR = ImGuiWrapper::COL_ORANGE_DARK; + static const ImVec4 SELECTION_RECT_COLOR = ImGuiWrapper::COL_ORANGE_DARK; static const ImVec4 COMMAND_COLOR = { 0.8f, 0.8f, 0.0f, 1.0f }; + static const ImVec4 PARAMETERS_COLOR = { 1.0f, 1.0f, 1.0f, 1.0f }; static const ImVec4 COMMENT_COLOR = { 0.7f, 0.7f, 0.7f, 1.0f }; +#if ENABLE_GCODE_WINDOW_USE_MAPPED_FILE + if (!m_visible || m_filename.empty() || m_lines_map.empty() || curr_line_id == 0) + return; +#else if (!m_visible || m_filename.empty() || m_gcode.empty() || curr_line_id == 0) return; +#endif // ENABLE_GCODE_WINDOW_USE_MAPPED_FILE // window height - const float text_height = ImGui::CalcTextSize("0").y; - const ImGuiStyle& style = ImGui::GetStyle(); const float wnd_height = bottom - top; // number of visible lines - const unsigned int lines_count = (wnd_height - 2.0f * style.WindowPadding.y + style.ItemSpacing.y) / (text_height + style.ItemSpacing.y); + const float text_height = ImGui::CalcTextSize("0").y; + const ImGuiStyle& style = ImGui::GetStyle(); + const uint64_t lines_count = static_cast((wnd_height - 2.0f * style.WindowPadding.y + style.ItemSpacing.y) / (text_height + style.ItemSpacing.y)); if (lines_count == 0) return; // visible range - const unsigned int half_lines_count = lines_count / 2; - unsigned int start_id = (curr_line_id >= half_lines_count) ? curr_line_id - half_lines_count : 0; - unsigned int end_id = start_id + lines_count - 1; - if (end_id >= m_file_size) { - end_id = m_file_size - 1; + const uint64_t half_lines_count = lines_count / 2; + uint64_t start_id = (curr_line_id >= half_lines_count) ? curr_line_id - half_lines_count : 0; + uint64_t end_id = start_id + lines_count - 1; +#if ENABLE_GCODE_WINDOW_USE_MAPPED_FILE + if (end_id >= static_cast(m_lines_map.size())) { + end_id = static_cast(m_lines_map.size()) - 1; start_id = end_id - lines_count + 1; } +#else + if (end_id >= static_cast(m_gcode.size())) { + end_id = static_cast(m_gcode.size()) - 1; + start_id = end_id - lines_count + 1; + } +#endif // ENABLE_GCODE_WINDOW_USE_MAPPED_FILE - if (m_last_line_id != curr_line_id || m_last_lines_size != static_cast(end_id - start_id + 1)) { - // updates list of lines to show + // updates list of lines to show, if needed + if (m_selected_line_id != curr_line_id || m_last_lines_size != end_id - start_id + 1) { +#if ENABLE_GCODE_WINDOW_USE_MAPPED_FILE + try + { + *const_cast*>(&m_lines) = update_lines(start_id, end_id); + } + catch (...) + { + BOOST_LOG_TRIVIAL(error) << "Error while loading from file " << m_filename << ". Cannot show G-code window."; + return; + } +#else *const_cast*>(&m_lines) = update_lines(start_id, end_id); - *const_cast(&m_last_line_id) = curr_line_id; - *const_cast(&m_last_lines_size) = m_lines.size(); +#endif // ENABLE_GCODE_WINDOW_USE_MAPPED_FILE + *const_cast(&m_selected_line_id) = curr_line_id; + *const_cast(&m_last_lines_size) = static_cast(m_lines.size()); } - // line id number column width + // line number's column width const float id_width = ImGui::CalcTextSize(std::to_string(end_id).c_str()).x; ImGuiWrapper& imgui = *wxGetApp().imgui(); @@ -408,28 +475,27 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u ImGui::SetNextWindowBgAlpha(0.6f); imgui.begin(std::string("G-code"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - // center the text in the window by pushing down the first line const float f_lines_count = static_cast(lines_count); ImGui::SetCursorPosY({ 0.5f * (wnd_height - f_lines_count * text_height - (f_lines_count - 1.0f) * style.ItemSpacing.y) }); // render text lines - for (unsigned int id = start_id; id <= end_id; ++id) { + for (uint64_t id = start_id; id <= end_id; ++id) { const Line& line = m_lines[id - start_id]; - // rect for the current selected move + // rect around the current selected line if (id == curr_line_id) { const float pos_y = ImGui::GetCursorScreenPos().y; const float half_ItemSpacing_y = 0.5f * style.ItemSpacing.y; const float half_padding_x = 0.5f * style.WindowPadding.x; - draw_list->AddRect({ half_padding_x, pos_y - half_ItemSpacing_y }, + ImGui::GetWindowDrawList()->AddRect({ half_padding_x, pos_y - half_ItemSpacing_y }, { ImGui::GetCurrentWindow()->Size.x - half_padding_x, pos_y + text_height + half_ItemSpacing_y }, - ImGui::GetColorU32(HIGHLIGHT_RECT_COLOR)); + ImGui::GetColorU32(SELECTION_RECT_COLOR)); } // render line number const std::string id_str = std::to_string(id); + // spacer to right align text ImGui::Dummy({ id_width - ImGui::CalcTextSize(id_str.c_str()).x, text_height }); ImGui::SameLine(0.0f, 0.0f); ImGui::PushStyleColor(ImGuiCol_Text, LINE_NUMBER_COLOR); @@ -446,10 +512,12 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u ImGui::PopStyleColor(); } - // render command parameters + // render parameters if (!line.parameters.empty()) { ImGui::SameLine(0.0f, 0.0f); + ImGui::PushStyleColor(ImGuiCol_Text, PARAMETERS_COLOR); imgui.text(line.parameters); + ImGui::PopStyleColor(); } // render comment @@ -466,13 +534,23 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u ImGui::PopStyleVar(); } +#if ENABLE_GCODE_WINDOW_USE_MAPPED_FILE +void GCodeViewer::SequentialView::GCodeWindow::stop_mapping_file() +{ + if (m_file.is_open()) { + m_file.close(); + std::cout << "closed file: " << m_filename << "\n"; + } +} +#endif // ENABLE_GCODE_WINDOW_USE_MAPPED_FILE + void GCodeViewer::SequentialView::render(float legend_height) const { marker.render(); float bottom = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size().get_height(); if (wxGetApp().is_editor()) bottom -= wxGetApp().plater()->get_view_toolbar().get_height(); - gcode_window.render(legend_height, bottom, gcode_ids[current.last]); + gcode_window.render(legend_height, bottom, static_cast(gcode_ids[current.last])); } #endif // ENABLE_GCODE_WINDOW diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 428a50e851..890961a82b 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -5,6 +5,10 @@ #include "libslic3r/GCode/GCodeProcessor.hpp" #include "GLModel.hpp" +#if ENABLE_GCODE_WINDOW_USE_MAPPED_FILE +#include +#endif // ENABLE_GCODE_WINDOW_USE_MAPPED_FILE + #include #include #include @@ -616,23 +620,49 @@ public: std::string comment; }; bool m_visible{ true }; - unsigned int m_file_size{ 0 }; - unsigned int m_last_line_id{ 0 }; + uint64_t m_selected_line_id{ 0 }; size_t m_last_lines_size{ 0 }; - std::vector m_lines; std::string m_filename; +#if ENABLE_GCODE_WINDOW_USE_MAPPED_FILE + boost::iostreams::mapped_file_source m_file; + // map for accessing data in file by line number + std::vector> m_lines_map; +#else std::vector m_gcode; +#endif // ENABLE_GCODE_WINDOW_USE_MAPPED_FILE + // current visible lines + std::vector m_lines; public: +#if ENABLE_GCODE_WINDOW_USE_MAPPED_FILE + GCodeWindow() = default; + ~GCodeWindow() { stop_mapping_file(); } +#endif // ENABLE_GCODE_WINDOW_USE_MAPPED_FILE void set_filename(const std::string& filename) { m_filename = filename; } void load_gcode(); +#if !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE void start_mapping_file(); void stop_mapping_file(); +#endif // !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE +#if ENABLE_GCODE_WINDOW_USE_MAPPED_FILE + void reset() { + stop_mapping_file(); + m_lines_map.clear(); + m_lines.clear(); + m_filename.clear(); + } +#else void reset() { m_filename.clear(); } +#endif // ENABLE_GCODE_WINDOW_USE_MAPPED_FILE void toggle_visibility() { m_visible = !m_visible; } - void render(float top, float bottom, unsigned int curr_line_id) const; + void render(float top, float bottom, uint64_t curr_line_id) const; + +#if ENABLE_GCODE_WINDOW_USE_MAPPED_FILE + private: + void stop_mapping_file(); +#endif // ENABLE_GCODE_WINDOW_USE_MAPPED_FILE }; #endif // ENABLE_GCODE_WINDOW @@ -753,8 +783,10 @@ public: void export_toolpaths_to_obj(const char* filename) const; #if ENABLE_GCODE_WINDOW +#if !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE void start_mapping_gcode_file() { m_sequential_view.gcode_window.start_mapping_file(); } void stop_mapping_gcode_file() { m_sequential_view.gcode_window.stop_mapping_file(); } +#endif // !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE void toggle_gcode_window_visibility() { m_sequential_view.gcode_window.toggle_visibility(); } #endif // ENABLE_GCODE_WINDOW diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 4931e73626..a6b6c636a5 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1196,7 +1196,9 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) GLCanvas3D::~GLCanvas3D() { #if ENABLE_GCODE_WINDOW +#if !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE m_gcode_viewer.stop_mapping_gcode_file(); +#endif // !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE #endif // ENABLE_GCODE_WINDOW reset_volumes(); } @@ -3903,6 +3905,7 @@ void GLCanvas3D::mouse_up_cleanup() } #if ENABLE_GCODE_WINDOW +#if !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE void GLCanvas3D::start_mapping_gcode_file() { m_gcode_viewer.start_mapping_gcode_file(); @@ -3912,6 +3915,7 @@ void GLCanvas3D::stop_mapping_gcode_file() { m_gcode_viewer.stop_mapping_gcode_file(); } +#endif // !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE #endif // ENABLE_GCODE_WINDOW bool GLCanvas3D::_is_shown_on_screen() const diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 990c8c048a..27e8b49c77 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -786,8 +786,10 @@ public: } #if ENABLE_GCODE_WINDOW +#if !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE void start_mapping_gcode_file(); void stop_mapping_gcode_file(); +#endif // !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE #endif // ENABLE_GCODE_WINDOW private: diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 4ca9834b9f..b3093a574c 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3379,7 +3379,9 @@ void Plater::priv::set_current_panel(wxPanel* panel) if (current_panel == view3D) { #if ENABLE_GCODE_WINDOW if (old_panel == preview) { +#if !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE preview->get_canvas3d()->stop_mapping_gcode_file(); +#endif // !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE preview->get_canvas3d()->unbind_event_handlers(); } #else @@ -3411,7 +3413,9 @@ void Plater::priv::set_current_panel(wxPanel* panel) preview->get_canvas3d()->bind_event_handlers(); #if ENABLE_GCODE_WINDOW +#if !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE preview->get_canvas3d()->start_mapping_gcode_file(); +#endif // !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE #endif // ENABLE_GCODE_WINDOW // see: Plater::priv::object_list_changed() From 9359d6d3d0a5f78bc2d05c42c5693725dfe049c4 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 8 Mar 2021 09:01:15 +0100 Subject: [PATCH 057/285] Follow-up of 0472c8452588809b382681ef1e418242f41b47e0 - Fixed syntax errors --- src/slic3r/GUI/GCodeViewer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index de17a4d8e2..4c934624dd 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -461,7 +461,7 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u *const_cast*>(&m_lines) = update_lines(start_id, end_id); #endif // ENABLE_GCODE_WINDOW_USE_MAPPED_FILE *const_cast(&m_selected_line_id) = curr_line_id; - *const_cast(&m_last_lines_size) = static_cast(m_lines.size()); + *const_cast(&m_last_lines_size) = m_lines.size(); } // line number's column width @@ -477,7 +477,7 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u // center the text in the window by pushing down the first line const float f_lines_count = static_cast(lines_count); - ImGui::SetCursorPosY({ 0.5f * (wnd_height - f_lines_count * text_height - (f_lines_count - 1.0f) * style.ItemSpacing.y) }); + ImGui::SetCursorPosY(0.5f * (wnd_height - f_lines_count * text_height - (f_lines_count - 1.0f) * style.ItemSpacing.y)); // render text lines for (uint64_t id = start_id; id <= end_id; ++id) { From 5998ee8f2e473ccb1d29aa976b04eec79b152fdd Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 8 Mar 2021 09:29:47 +0100 Subject: [PATCH 058/285] Code cleanup --- src/slic3r/GUI/GCodeViewer.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 4c934624dd..259f378992 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -341,9 +341,6 @@ void GCodeViewer::SequentialView::GCodeWindow::load_gcode() try { m_file.open(boost::filesystem::path(m_filename)); - if (m_file.is_open()) { - std::cout << "open file: " << m_filename << "\n"; - } } catch (...) { @@ -537,10 +534,8 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u #if ENABLE_GCODE_WINDOW_USE_MAPPED_FILE void GCodeViewer::SequentialView::GCodeWindow::stop_mapping_file() { - if (m_file.is_open()) { + if (m_file.is_open()) m_file.close(); - std::cout << "closed file: " << m_filename << "\n"; - } } #endif // ENABLE_GCODE_WINDOW_USE_MAPPED_FILE From 589d2be442d93e019c7fc8ccf26fa15d6f61be5d Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 8 Mar 2021 09:36:13 +0100 Subject: [PATCH 059/285] Fix of Repair with Netfabb does not work on builds after 2.3.0 release (Windows 10) #6193 This is more a workaround than a fix: Windows 10 3D model fixing API refuses to load a zip64 encoded 3MF. We need to get in touch with Microsoft on that issue, for now the 3MFs generated for the Windows 10 3D model fixing API will be limited to 4GB. Saving a bigger 3MF will fail. --- src/libslic3r/Format/3mf.cpp | 19 +++++++++++++------ src/libslic3r/Format/3mf.hpp | 2 +- src/slic3r/Utils/FixModelByWin10.cpp | 2 +- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index e10b26f383..8ba248998a 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -2014,9 +2014,10 @@ namespace Slic3r { typedef std::map IdToObjectDataMap; bool m_fullpath_sources{ true }; + bool m_zip64 { true }; public: - bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data = nullptr); + bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data, bool zip64); private: bool _save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data); @@ -2036,10 +2037,11 @@ namespace Slic3r { bool _add_custom_gcode_per_print_z_file_to_archive(mz_zip_archive& archive, Model& model, const DynamicPrintConfig* config); }; - bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data) + bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data, bool zip64) { clear_errors(); m_fullpath_sources = fullpath_sources; + m_zip64 = zip64; return _save_model_to_file(filename, model, config, thumbnail_data); } @@ -2233,9 +2235,13 @@ namespace Slic3r { { mz_zip_writer_staged_context context; if (!mz_zip_writer_add_staged_open(&archive, &context, MODEL_FILE.c_str(), - // Maximum expected and allowed 3MF file size is 16GiB. - // This switches the ZIP file to a 64bit mode, which adds a tiny bit of overhead to file records. - (uint64_t(1) << 30) * 16, + m_zip64 ? + // Maximum expected and allowed 3MF file size is 16GiB. + // This switches the ZIP file to a 64bit mode, which adds a tiny bit of overhead to file records. + (uint64_t(1) << 30) * 16 : + // Maximum expected 3MF file size is 4GB-1. This is a workaround for interoperability with Windows 10 3D model fixing API, see + // GH issue #6193. + (uint64_t(1) << 32) - 1, nullptr, nullptr, 0, MZ_DEFAULT_COMPRESSION, nullptr, 0, nullptr, 0)) { add_error("Unable to add model file to archive"); return false; @@ -2926,12 +2932,13 @@ bool load_3mf(const char* path, DynamicPrintConfig* config, Model* model, bool c return res; } -bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data) +bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data, bool zip64) { if (path == nullptr || model == nullptr) return false; _3MF_Exporter exporter; + exporter.zip64 = zip64; bool res = exporter.save_model_to_file(path, *model, config, fullpath_sources, thumbnail_data); if (!res) exporter.log_errors(); diff --git a/src/libslic3r/Format/3mf.hpp b/src/libslic3r/Format/3mf.hpp index ccfd9356d8..a09a1b8344 100644 --- a/src/libslic3r/Format/3mf.hpp +++ b/src/libslic3r/Format/3mf.hpp @@ -33,7 +33,7 @@ namespace Slic3r { // Save the given model and the config data contained in the given Print into a 3mf file. // The model could be modified during the export process if meshes are not repaired or have no shared vertices - extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data = nullptr); + extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data = nullptr, bool zip64 = true); } // namespace Slic3r diff --git a/src/slic3r/Utils/FixModelByWin10.cpp b/src/slic3r/Utils/FixModelByWin10.cpp index bcab6daaf8..7933a1d694 100644 --- a/src/slic3r/Utils/FixModelByWin10.cpp +++ b/src/slic3r/Utils/FixModelByWin10.cpp @@ -363,7 +363,7 @@ void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx) ModelObject *model_object = model.add_object(); model_object->add_volume(*volumes[ivolume]); model_object->add_instance(); - if (!Slic3r::store_3mf(path_src.string().c_str(), &model, nullptr, false)) { + if (!Slic3r::store_3mf(path_src.string().c_str(), &model, nullptr, false, nullptr, false)) { boost::filesystem::remove(path_src); throw Slic3r::RuntimeError(L("Export of a temporary 3mf file failed")); } From ed7be17bf1142bffbe0f99e95a01270225235626 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 8 Mar 2021 09:38:14 +0100 Subject: [PATCH 060/285] Detection of Win10 3D printing API from the default Visual Studio env variables. --- CMakeLists.txt | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 98ae4f0c68..a3b944532e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -146,21 +146,33 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON) # WIN10SDK_PATH is used to point CMake to the WIN10 SDK installation directory. # We pick it from environment if it is not defined in another way if(WIN32) - if(NOT DEFINED WIN10SDK_PATH) - if(DEFINED ENV{WIN10SDK_PATH}) - set(WIN10SDK_PATH "$ENV{WIN10SDK_PATH}") - endif() + if(NOT DEFINED WIN10SDK_PATH) + if(DEFINED ENV{WIN10SDK_PATH}) + set(WIN10SDK_PATH "$ENV{WIN10SDK_PATH}") endif() - if(DEFINED WIN10SDK_PATH AND NOT EXISTS "${WIN10SDK_PATH}/include/winrt/windows.graphics.printing3d.h") - message("WIN10SDK_PATH is invalid: ${WIN10SDK_PATH}") - message("${WIN10SDK_PATH}/include/winrt/windows.graphics.printing3d.h was not found") - message("STL fixing by the Netfabb service will not be compiled") - unset(WIN10SDK_PATH) + endif() + if(DEFINED WIN10SDK_PATH) + if (EXISTS "${WIN10SDK_PATH}/include/winrt/windows.graphics.printing3d.h") + set(WIN10SDK_INCLUDE_PATH "${WIN10SDK_PATH}/Include") + else() + message("WIN10SDK_PATH is invalid: ${WIN10SDK_PATH}") + message("${WIN10SDK_PATH}/include/winrt/windows.graphics.printing3d.h was not found") + message("STL fixing by the Netfabb service will not be compiled") + unset(WIN10SDK_PATH) endif() - if(WIN10SDK_PATH) + else() + # Try to use the default Windows 10 SDK path. + set(WIN10SDK_INCLUDE_PATH "$ENV{WindowsSdkDir}/Include/$ENV{WindowsSDKVersion}") + if (NOT EXISTS "${WIN10SDK_INCLUDE_PATH}/winrt/windows.graphics.printing3d.h") + message("${WIN10SDK_INCLUDE_PATH}/winrt/windows.graphics.printing3d.h was not found") + message("STL fixing by the Netfabb service will not be compiled") + unset(WIN10SDK_INCLUDE_PATH) + endif() + endif() + if(WIN10SDK_INCLUDE_PATH) message("Building with Win10 Netfabb STL fixing service support") add_definitions(-DHAS_WIN10SDK) - include_directories("${WIN10SDK_PATH}/Include") + include_directories("${WIN10SDK_INCLUDE_PATH}") else() message("Building without Win10 Netfabb STL fixing service support") endif() From 14aca210cb24e093fff0063a781b46075e6d3557 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 8 Mar 2021 10:20:07 +0100 Subject: [PATCH 061/285] Tech ENABLE_GCODE_WINDOW_USE_MAPPED_FILE merged into ENABLE_GCODE_WINDOW --- src/libslic3r/Technologies.hpp | 2 -- src/slic3r/GUI/GCodeViewer.cpp | 47 +--------------------------------- src/slic3r/GUI/GCodeViewer.hpp | 24 ++--------------- src/slic3r/GUI/GLCanvas3D.cpp | 19 -------------- src/slic3r/GUI/GLCanvas3D.hpp | 7 ----- src/slic3r/GUI/Plater.cpp | 14 ---------- 6 files changed, 3 insertions(+), 110 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index a4c6aa214a..63ee79805d 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -55,8 +55,6 @@ #define ENABLE_VALIDATE_CUSTOM_GCODE (1 && ENABLE_2_3_1_ALPHA1) // Enable showing a imgui window containing gcode in preview #define ENABLE_GCODE_WINDOW (1 && ENABLE_2_3_1_ALPHA1) -// Enable using file mapping to show a imgui window containing gcode in preview -#define ENABLE_GCODE_WINDOW_USE_MAPPED_FILE (1 && ENABLE_GCODE_WINDOW) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 259f378992..7abcbdc41a 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -303,9 +303,6 @@ void GCodeViewer::SequentialView::Marker::render() const #if ENABLE_GCODE_WINDOW void GCodeViewer::SequentialView::GCodeWindow::load_gcode() { -#if !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE - m_gcode.clear(); -#endif // !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE if (m_filename.empty()) return; @@ -313,19 +310,13 @@ void GCodeViewer::SequentialView::GCodeWindow::load_gcode() { boost::nowide::ifstream f(m_filename); std::string line; -#if ENABLE_GCODE_WINDOW_USE_MAPPED_FILE // generate mapping for accessing data in file by line number uint64_t offset = 0; -#endif // ENABLE_GCODE_WINDOW_USE_MAPPED_FILE while (std::getline(f, line)) { -#if ENABLE_GCODE_WINDOW_USE_MAPPED_FILE size_t line_length = static_cast(line.length()); m_lines_map.push_back({ offset, line_length }); offset += static_cast(line_length) + 1; -#else - m_gcode.push_back(line); -#endif // ENABLE_GCODE_WINDOW_USE_MAPPED_FILE - } + } } catch (...) { @@ -337,7 +328,6 @@ void GCodeViewer::SequentialView::GCodeWindow::load_gcode() m_selected_line_id = 0; m_last_lines_size = 0; -#if ENABLE_GCODE_WINDOW_USE_MAPPED_FILE try { m_file.open(boost::filesystem::path(m_filename)); @@ -347,33 +337,16 @@ void GCodeViewer::SequentialView::GCodeWindow::load_gcode() BOOST_LOG_TRIVIAL(error) << "Unable to map file " << m_filename << ". Cannot show G-code window."; reset(); } -#endif // ENABLE_GCODE_WINDOW_USE_MAPPED_FILE } -#if !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE -void GCodeViewer::SequentialView::GCodeWindow::start_mapping_file() -{ - std::cout << "GCodeViewer::SequentialView::GCodeWindow::start_mapping_file()\n"; -} - -void GCodeViewer::SequentialView::GCodeWindow::stop_mapping_file() -{ - std::cout << "GCodeViewer::SequentialView::GCodeWindow::stop_mapping_file()\n"; -} -#endif // !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE - void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, uint64_t curr_line_id) const { auto update_lines = [this](uint64_t start_id, uint64_t end_id) { std::vector ret; ret.reserve(end_id - start_id + 1); for (uint64_t id = start_id; id <= end_id; ++id) { -#if ENABLE_GCODE_WINDOW_USE_MAPPED_FILE // read line from file std::string gline(m_file.data() + m_lines_map[id - 1].first, m_lines_map[id - 1].second); -#else - const std::string& gline = m_gcode[id - 1]; -#endif // ENABLE_GCODE_WINDOW_USE_MAPPED_FILE std::string command; std::string parameters; @@ -407,13 +380,8 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u static const ImVec4 PARAMETERS_COLOR = { 1.0f, 1.0f, 1.0f, 1.0f }; static const ImVec4 COMMENT_COLOR = { 0.7f, 0.7f, 0.7f, 1.0f }; -#if ENABLE_GCODE_WINDOW_USE_MAPPED_FILE if (!m_visible || m_filename.empty() || m_lines_map.empty() || curr_line_id == 0) return; -#else - if (!m_visible || m_filename.empty() || m_gcode.empty() || curr_line_id == 0) - return; -#endif // ENABLE_GCODE_WINDOW_USE_MAPPED_FILE // window height const float wnd_height = bottom - top; @@ -430,21 +398,13 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u const uint64_t half_lines_count = lines_count / 2; uint64_t start_id = (curr_line_id >= half_lines_count) ? curr_line_id - half_lines_count : 0; uint64_t end_id = start_id + lines_count - 1; -#if ENABLE_GCODE_WINDOW_USE_MAPPED_FILE if (end_id >= static_cast(m_lines_map.size())) { end_id = static_cast(m_lines_map.size()) - 1; start_id = end_id - lines_count + 1; } -#else - if (end_id >= static_cast(m_gcode.size())) { - end_id = static_cast(m_gcode.size()) - 1; - start_id = end_id - lines_count + 1; - } -#endif // ENABLE_GCODE_WINDOW_USE_MAPPED_FILE // updates list of lines to show, if needed if (m_selected_line_id != curr_line_id || m_last_lines_size != end_id - start_id + 1) { -#if ENABLE_GCODE_WINDOW_USE_MAPPED_FILE try { *const_cast*>(&m_lines) = update_lines(start_id, end_id); @@ -454,9 +414,6 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u BOOST_LOG_TRIVIAL(error) << "Error while loading from file " << m_filename << ". Cannot show G-code window."; return; } -#else - *const_cast*>(&m_lines) = update_lines(start_id, end_id); -#endif // ENABLE_GCODE_WINDOW_USE_MAPPED_FILE *const_cast(&m_selected_line_id) = curr_line_id; *const_cast(&m_last_lines_size) = m_lines.size(); } @@ -531,13 +488,11 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u ImGui::PopStyleVar(); } -#if ENABLE_GCODE_WINDOW_USE_MAPPED_FILE void GCodeViewer::SequentialView::GCodeWindow::stop_mapping_file() { if (m_file.is_open()) m_file.close(); } -#endif // ENABLE_GCODE_WINDOW_USE_MAPPED_FILE void GCodeViewer::SequentialView::render(float legend_height) const { diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 890961a82b..cc897c7bcd 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -5,9 +5,9 @@ #include "libslic3r/GCode/GCodeProcessor.hpp" #include "GLModel.hpp" -#if ENABLE_GCODE_WINDOW_USE_MAPPED_FILE +#if ENABLE_GCODE_WINDOW #include -#endif // ENABLE_GCODE_WINDOW_USE_MAPPED_FILE +#endif // ENABLE_GCODE_WINDOW #include #include @@ -623,46 +623,30 @@ public: uint64_t m_selected_line_id{ 0 }; size_t m_last_lines_size{ 0 }; std::string m_filename; -#if ENABLE_GCODE_WINDOW_USE_MAPPED_FILE boost::iostreams::mapped_file_source m_file; // map for accessing data in file by line number std::vector> m_lines_map; -#else - std::vector m_gcode; -#endif // ENABLE_GCODE_WINDOW_USE_MAPPED_FILE // current visible lines std::vector m_lines; public: -#if ENABLE_GCODE_WINDOW_USE_MAPPED_FILE GCodeWindow() = default; ~GCodeWindow() { stop_mapping_file(); } -#endif // ENABLE_GCODE_WINDOW_USE_MAPPED_FILE void set_filename(const std::string& filename) { m_filename = filename; } void load_gcode(); -#if !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE - void start_mapping_file(); - void stop_mapping_file(); -#endif // !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE -#if ENABLE_GCODE_WINDOW_USE_MAPPED_FILE void reset() { stop_mapping_file(); m_lines_map.clear(); m_lines.clear(); m_filename.clear(); } -#else - void reset() { m_filename.clear(); } -#endif // ENABLE_GCODE_WINDOW_USE_MAPPED_FILE void toggle_visibility() { m_visible = !m_visible; } void render(float top, float bottom, uint64_t curr_line_id) const; -#if ENABLE_GCODE_WINDOW_USE_MAPPED_FILE private: void stop_mapping_file(); -#endif // ENABLE_GCODE_WINDOW_USE_MAPPED_FILE }; #endif // ENABLE_GCODE_WINDOW @@ -783,10 +767,6 @@ public: void export_toolpaths_to_obj(const char* filename) const; #if ENABLE_GCODE_WINDOW -#if !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE - void start_mapping_gcode_file() { m_sequential_view.gcode_window.start_mapping_file(); } - void stop_mapping_gcode_file() { m_sequential_view.gcode_window.stop_mapping_file(); } -#endif // !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE void toggle_gcode_window_visibility() { m_sequential_view.gcode_window.toggle_visibility(); } #endif // ENABLE_GCODE_WINDOW diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 8f78e63e2e..a775815351 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1195,11 +1195,6 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) GLCanvas3D::~GLCanvas3D() { -#if ENABLE_GCODE_WINDOW -#if !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE - m_gcode_viewer.stop_mapping_gcode_file(); -#endif // !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE -#endif // ENABLE_GCODE_WINDOW reset_volumes(); } @@ -3900,20 +3895,6 @@ void GLCanvas3D::mouse_up_cleanup() m_canvas->ReleaseMouse(); } -#if ENABLE_GCODE_WINDOW -#if !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE -void GLCanvas3D::start_mapping_gcode_file() -{ - m_gcode_viewer.start_mapping_gcode_file(); -} - -void GLCanvas3D::stop_mapping_gcode_file() -{ - m_gcode_viewer.stop_mapping_gcode_file(); -} -#endif // !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE -#endif // ENABLE_GCODE_WINDOW - bool GLCanvas3D::_is_shown_on_screen() const { return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 27e8b49c77..10294931fe 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -785,13 +785,6 @@ public: #endif } -#if ENABLE_GCODE_WINDOW -#if !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE - void start_mapping_gcode_file(); - void stop_mapping_gcode_file(); -#endif // !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE -#endif // ENABLE_GCODE_WINDOW - private: bool _is_shown_on_screen() const; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 67aae3f9b8..7a38dd3a1c 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3368,17 +3368,8 @@ void Plater::priv::set_current_panel(wxPanel* panel) panel_sizer->Layout(); if (current_panel == view3D) { -#if ENABLE_GCODE_WINDOW - if (old_panel == preview) { -#if !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE - preview->get_canvas3d()->stop_mapping_gcode_file(); -#endif // !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE - preview->get_canvas3d()->unbind_event_handlers(); - } -#else if (old_panel == preview) preview->get_canvas3d()->unbind_event_handlers(); -#endif // ENABLE_GCODE_WINDOW view3D->get_canvas3d()->bind_event_handlers(); @@ -3403,11 +3394,6 @@ void Plater::priv::set_current_panel(wxPanel* panel) view3D->get_canvas3d()->unbind_event_handlers(); preview->get_canvas3d()->bind_event_handlers(); -#if ENABLE_GCODE_WINDOW -#if !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE - preview->get_canvas3d()->start_mapping_gcode_file(); -#endif // !ENABLE_GCODE_WINDOW_USE_MAPPED_FILE -#endif // ENABLE_GCODE_WINDOW // see: Plater::priv::object_list_changed() // FIXME: it may be better to have a single function making this check and let it be called wherever needed From 8adb495e7dad4759d5a3a37089af1095cf9056e3 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 8 Mar 2021 11:29:52 +0100 Subject: [PATCH 062/285] Fix of 589d2be442d93e019c7fc8ccf26fa15d6f61be5d --- src/libslic3r/Format/3mf.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 8ba248998a..152d72079f 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -2938,8 +2938,7 @@ bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, return false; _3MF_Exporter exporter; - exporter.zip64 = zip64; - bool res = exporter.save_model_to_file(path, *model, config, fullpath_sources, thumbnail_data); + bool res = exporter.save_model_to_file(path, *model, config, fullpath_sources, thumbnail_data, zip64); if (!res) exporter.log_errors(); From 7cb3e729eee70d708396e0f17f657c49ac45757f Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 8 Mar 2021 14:19:58 +0100 Subject: [PATCH 063/285] Fixed #6182 - First line custom gcode not aligned left in the tool tip --- src/slic3r/GUI/DoubleSlider.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index c12d361120..81945061b0 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -1317,7 +1317,12 @@ wxString Control::get_tooltip(int tick/*=-1*/) "This code won't be processed during G-code generation."); // Show custom Gcode as a first string of tooltop - tooltip = " "; + std::string space = " "; + tooltip = space; + auto format_gcode = [space](std::string gcode) { + boost::replace_all(gcode, "\n", "\n" + space); + return gcode; + }; tooltip += tick_code_it->type == ColorChange ? (m_mode == SingleExtruder ? @@ -1329,7 +1334,7 @@ wxString Control::get_tooltip(int tick/*=-1*/) format_wxstr(_L("Custom template (\"%1%\")"), gcode(Template)) : tick_code_it->type == ToolChange ? format_wxstr(_L("Extruder (tool) is changed to Extruder \"%1%\""), tick_code_it->extruder) : - from_u8(tick_code_it->extra);// tick_code_it->type == Custom + from_u8(format_gcode(tick_code_it->extra));// tick_code_it->type == Custom // If tick is marked as a conflict (exclamation icon), // we should to explain why From e57eca0289e2f5e3e6bb2f6d6fa53e0fc48987c3 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 17 Dec 2020 13:38:09 +0100 Subject: [PATCH 064/285] Add voxel scale to openvdb metadata. To be able to retrieve that information from a generated grid alone. To avoid the copying of input mesh (for scaling) when doing the hollowing Also remove some unused stuff from OpenVDBUtils --- src/libslic3r/OpenVDBUtils.cpp | 110 +++++++++++++------------------- src/libslic3r/OpenVDBUtils.hpp | 14 ++-- src/libslic3r/SLA/Hollowing.cpp | 36 +++++------ 3 files changed, 70 insertions(+), 90 deletions(-) diff --git a/src/libslic3r/OpenVDBUtils.cpp b/src/libslic3r/OpenVDBUtils.cpp index 0f5bfa157c..259cd1cbdc 100644 --- a/src/libslic3r/OpenVDBUtils.cpp +++ b/src/libslic3r/OpenVDBUtils.cpp @@ -22,74 +22,52 @@ namespace Slic3r { class TriangleMeshDataAdapter { public: const TriangleMesh &mesh; - + float voxel_scale; + size_t polygonCount() const { return mesh.its.indices.size(); } size_t pointCount() const { return mesh.its.vertices.size(); } size_t vertexCount(size_t) const { return 3; } - + // Return position pos in local grid index space for polygon n and vertex v - void getIndexSpacePoint(size_t n, size_t v, openvdb::Vec3d& pos) const; + // The actual mesh will appear to openvdb as scaled uniformly by voxel_size + // And the voxel count per unit volume can be affected this way. + void getIndexSpacePoint(size_t n, size_t v, openvdb::Vec3d& pos) const + { + auto vidx = size_t(mesh.its.indices[n](Eigen::Index(v))); + Slic3r::Vec3d p = mesh.its.vertices[vidx].cast() * voxel_scale; + pos = {p.x(), p.y(), p.z()}; + } + + TriangleMeshDataAdapter(const TriangleMesh &m, float voxel_sc = 1.f) + : mesh{m}, voxel_scale{voxel_sc} {}; }; -class Contour3DDataAdapter { -public: - const sla::Contour3D &mesh; - - size_t polygonCount() const { return mesh.faces3.size() + mesh.faces4.size(); } - size_t pointCount() const { return mesh.points.size(); } - size_t vertexCount(size_t n) const { return n < mesh.faces3.size() ? 3 : 4; } - - // Return position pos in local grid index space for polygon n and vertex v - void getIndexSpacePoint(size_t n, size_t v, openvdb::Vec3d& pos) const; -}; - -void TriangleMeshDataAdapter::getIndexSpacePoint(size_t n, - size_t v, - openvdb::Vec3d &pos) const -{ - auto vidx = size_t(mesh.its.indices[n](Eigen::Index(v))); - Slic3r::Vec3d p = mesh.its.vertices[vidx].cast(); - pos = {p.x(), p.y(), p.z()}; -} - -void Contour3DDataAdapter::getIndexSpacePoint(size_t n, - size_t v, - openvdb::Vec3d &pos) const -{ - size_t vidx = 0; - if (n < mesh.faces3.size()) vidx = size_t(mesh.faces3[n](Eigen::Index(v))); - else vidx = size_t(mesh.faces4[n - mesh.faces3.size()](Eigen::Index(v))); - - Slic3r::Vec3d p = mesh.points[vidx]; - pos = {p.x(), p.y(), p.z()}; -} - - // TODO: Do I need to call initialize? Seems to work without it as well but the // docs say it should be called ones. It does a mutex lock-unlock sequence all // even if was called previously. -openvdb::FloatGrid::Ptr mesh_to_grid(const TriangleMesh &mesh, +openvdb::FloatGrid::Ptr mesh_to_grid(const TriangleMesh & mesh, const openvdb::math::Transform &tr, - float exteriorBandWidth, - float interiorBandWidth, - int flags) + float voxel_scale, + float exteriorBandWidth, + float interiorBandWidth, + int flags) { openvdb::initialize(); TriangleMeshPtrs meshparts = mesh.split(); auto it = std::remove_if(meshparts.begin(), meshparts.end(), - [](TriangleMesh *m){ - m->require_shared_vertices(); - return !m->is_manifold() || m->volume() < EPSILON; - }); + [](TriangleMesh *m){ + m->require_shared_vertices(); + return !m->is_manifold() || m->volume() < EPSILON; + }); meshparts.erase(it, meshparts.end()); openvdb::FloatGrid::Ptr grid; for (TriangleMesh *m : meshparts) { auto subgrid = openvdb::tools::meshToVolume( - TriangleMeshDataAdapter{*m}, tr, exteriorBandWidth, + TriangleMeshDataAdapter{*m, voxel_scale}, tr, exteriorBandWidth, interiorBandWidth, flags); if (grid && subgrid) openvdb::tools::csgUnion(*grid, *subgrid); @@ -106,19 +84,9 @@ openvdb::FloatGrid::Ptr mesh_to_grid(const TriangleMesh &mesh, interiorBandWidth, flags); } - return grid; -} + grid->insertMeta("voxel_scale", openvdb::FloatMetadata(voxel_scale)); -openvdb::FloatGrid::Ptr mesh_to_grid(const sla::Contour3D &mesh, - const openvdb::math::Transform &tr, - float exteriorBandWidth, - float interiorBandWidth, - int flags) -{ - openvdb::initialize(); - return openvdb::tools::meshToVolume( - Contour3DDataAdapter{mesh}, tr, exteriorBandWidth, interiorBandWidth, - flags); + return grid; } template @@ -128,20 +96,25 @@ sla::Contour3D _volumeToMesh(const Grid &grid, bool relaxDisorientedTriangles) { openvdb::initialize(); - + std::vector points; std::vector triangles; std::vector quads; - + openvdb::tools::volumeToMesh(grid, points, triangles, quads, isovalue, adaptivity, relaxDisorientedTriangles); - + + float scale = 1.; + try { + scale = grid.template metaValue("voxel_scale"); + } catch (...) { } + sla::Contour3D ret; ret.points.reserve(points.size()); ret.faces3.reserve(triangles.size()); ret.faces4.reserve(quads.size()); - for (auto &v : points) ret.points.emplace_back(to_vec3d(v)); + for (auto &v : points) ret.points.emplace_back(to_vec3d(v) / scale); for (auto &v : triangles) ret.faces3.emplace_back(to_vec3i(v)); for (auto &v : quads) ret.faces4.emplace_back(to_vec4i(v)); @@ -166,9 +139,18 @@ sla::Contour3D grid_to_contour3d(const openvdb::FloatGrid &grid, relaxDisorientedTriangles); } -openvdb::FloatGrid::Ptr redistance_grid(const openvdb::FloatGrid &grid, double iso, double er, double ir) +openvdb::FloatGrid::Ptr redistance_grid(const openvdb::FloatGrid &grid, + double iso, + double er, + double ir) { - return openvdb::tools::levelSetRebuild(grid, float(iso), float(er), float(ir)); + auto new_grid = openvdb::tools::levelSetRebuild(grid, float(iso), + float(er), float(ir)); + + // Copies voxel_scale metadata, if it exists. + new_grid->insertMeta(*grid.deepCopyMeta()); + + return new_grid; } } // namespace Slic3r diff --git a/src/libslic3r/OpenVDBUtils.hpp b/src/libslic3r/OpenVDBUtils.hpp index aa4b5154a8..5df816abb1 100644 --- a/src/libslic3r/OpenVDBUtils.hpp +++ b/src/libslic3r/OpenVDBUtils.hpp @@ -21,14 +21,16 @@ inline Vec3d to_vec3d(const openvdb::Vec3s &v) { return to_vec3f(v).cast inline Vec3i to_vec3i(const openvdb::Vec3I &v) { return Vec3i{int(v[0]), int(v[1]), int(v[2])}; } inline Vec4i to_vec4i(const openvdb::Vec4I &v) { return Vec4i{int(v[0]), int(v[1]), int(v[2]), int(v[3])}; } +// Here voxel_scale defines the scaling of voxels which affects the voxel count. +// 1.0 value means a voxel for every unit cube. 2 means the model is scaled to +// be 2x larger and the voxel count is increased by the increment in the scaled +// volume, thus 4 times. This kind a sampling accuracy selection is not +// achievable through the Transform parameter. (TODO: or is it?) +// The resulting grid will contain the voxel_scale in its metadata under the +// "voxel_scale" key to be used in grid_to_mesh function. openvdb::FloatGrid::Ptr mesh_to_grid(const TriangleMesh & mesh, const openvdb::math::Transform &tr = {}, - float exteriorBandWidth = 3.0f, - float interiorBandWidth = 3.0f, - int flags = 0); - -openvdb::FloatGrid::Ptr mesh_to_grid(const sla::Contour3D & mesh, - const openvdb::math::Transform &tr = {}, + float voxel_scale = 1.f, float exteriorBandWidth = 3.0f, float interiorBandWidth = 3.0f, int flags = 0); diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index 6df752fd36..61e1e122b2 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -32,48 +32,44 @@ static TriangleMesh _generate_interior(const TriangleMesh &mesh, double voxel_scale, double closing_dist) { - TriangleMesh imesh{mesh}; - - _scale(voxel_scale, imesh); - double offset = voxel_scale * min_thickness; double D = voxel_scale * closing_dist; float out_range = 0.1f * float(offset); float in_range = 1.1f * float(offset + D); - + if (ctl.stopcondition()) return {}; else ctl.statuscb(0, L("Hollowing")); - - auto gridptr = mesh_to_grid(imesh, {}, out_range, in_range); - + + auto gridptr = mesh_to_grid(mesh, {}, voxel_scale, out_range, in_range); + assert(gridptr); - + if (!gridptr) { BOOST_LOG_TRIVIAL(error) << "Returned OpenVDB grid is NULL"; return {}; } - + if (ctl.stopcondition()) return {}; else ctl.statuscb(30, L("Hollowing")); - + + double iso_surface = D; + auto narrowb = double(in_range); if (closing_dist > .0) { - gridptr = redistance_grid(*gridptr, -(offset + D), double(in_range)); + gridptr = redistance_grid(*gridptr, -(offset + D), narrowb, narrowb); } else { - D = -offset; + iso_surface = -offset; } - + if (ctl.stopcondition()) return {}; else ctl.statuscb(70, L("Hollowing")); - - double iso_surface = D; + double adaptivity = 0.; + auto omesh = grid_to_mesh(*gridptr, iso_surface, adaptivity); - - _scale(1. / voxel_scale, omesh); - + if (ctl.stopcondition()) return {}; else ctl.statuscb(100, L("Hollowing")); - + return omesh; } From 82954ba7152dc05f76445614e17ed97edbe0091e Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 17 Dec 2020 16:38:04 +0100 Subject: [PATCH 065/285] Group hollowing result (including grid) into one struct --- src/libslic3r/SLA/Hollowing.cpp | 104 +++++++++++++++++++++-------- src/libslic3r/SLA/Hollowing.hpp | 23 +++++-- src/libslic3r/SLAPrint.cpp | 5 +- src/libslic3r/SLAPrint.hpp | 4 +- src/libslic3r/SLAPrintSteps.cpp | 28 +++++--- tests/libslic3r/test_hollowing.cpp | 30 ++++----- tests/sla_print/sla_test_utils.cpp | 6 +- 7 files changed, 133 insertions(+), 67 deletions(-) diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index 61e1e122b2..44358cebef 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -26,11 +26,36 @@ inline void _scale(S s, TriangleMesh &m) { m.scale(float(s)); } template> inline void _scale(S s, Contour3D &m) { for (auto &p : m.points) p *= s; } -static TriangleMesh _generate_interior(const TriangleMesh &mesh, - const JobController &ctl, - double min_thickness, - double voxel_scale, - double closing_dist) +struct Interior { + TriangleMesh mesh; + openvdb::FloatGrid::Ptr gridptr; + double closing_distance = 0.; + double thickness = 0.; + double voxel_scale = 1.; + double nb_in = 3.; + double nb_out = 3.; +}; + +void InteriorDeleter::operator()(Interior *p) +{ + delete p; +} + +TriangleMesh &get_mesh(Interior &interior) +{ + return interior.mesh; +} + +const TriangleMesh &get_mesh(const Interior &interior) +{ + return interior.mesh; +} + +static InteriorPtr generate_interior_verbose(const TriangleMesh & mesh, + const JobController &ctl, + double min_thickness, + double voxel_scale, + double closing_dist) { double offset = voxel_scale * min_thickness; double D = voxel_scale * closing_dist; @@ -64,22 +89,30 @@ static TriangleMesh _generate_interior(const TriangleMesh &mesh, else ctl.statuscb(70, L("Hollowing")); double adaptivity = 0.; + InteriorPtr interior = InteriorPtr{new Interior{}}; - auto omesh = grid_to_mesh(*gridptr, iso_surface, adaptivity); + interior->mesh = grid_to_mesh(*gridptr, iso_surface, adaptivity); + interior->gridptr = gridptr; if (ctl.stopcondition()) return {}; else ctl.statuscb(100, L("Hollowing")); - return omesh; + interior->closing_distance = D; + interior->thickness = offset; + interior->voxel_scale = voxel_scale; + interior->nb_in = narrowb; + interior->nb_out = narrowb; + + return interior; } -std::unique_ptr generate_interior(const TriangleMesh & mesh, - const HollowingConfig &hc, - const JobController & ctl) +InteriorPtr generate_interior(const TriangleMesh & mesh, + const HollowingConfig &hc, + const JobController & ctl) { static const double MIN_OVERSAMPL = 3.; static const double MAX_OVERSAMPL = 8.; - + // I can't figure out how to increase the grid resolution through openvdb // API so the model will be scaled up before conversion and the result // scaled down. Voxels have a unit size. If I set voxelSize smaller, it @@ -88,26 +121,29 @@ std::unique_ptr generate_interior(const TriangleMesh & mesh, // // max 8x upscale, min is native voxel size auto voxel_scale = MIN_OVERSAMPL + (MAX_OVERSAMPL - MIN_OVERSAMPL) * hc.quality; - auto meshptr = std::make_unique( - _generate_interior(mesh, ctl, hc.min_thickness, voxel_scale, - hc.closing_distance)); - - if (meshptr && !meshptr->empty()) { - + + InteriorPtr interior = + generate_interior_verbose(mesh, ctl, hc.min_thickness, voxel_scale, + hc.closing_distance); + + if (interior && !interior->mesh.empty()) { + // This flips the normals to be outward facing... - meshptr->require_shared_vertices(); - indexed_triangle_set its = std::move(meshptr->its); - + interior->mesh.require_shared_vertices(); + indexed_triangle_set its = std::move(interior->mesh.its); + Slic3r::simplify_mesh(its); - + // flip normals back... for (stl_triangle_vertex_indices &ind : its.indices) std::swap(ind(0), ind(2)); - - *meshptr = Slic3r::TriangleMesh{its}; + + interior->mesh = Slic3r::TriangleMesh{its}; + interior->mesh.repaired = true; + interior->mesh.require_shared_vertices(); } - - return meshptr; + + return interior; } Contour3D DrainHole::to_mesh() const @@ -269,12 +305,22 @@ void cut_drainholes(std::vector & obj_slices, obj_slices[i] = diff_ex(obj_slices[i], hole_slices[i]); } -void hollow_mesh(TriangleMesh &mesh, const HollowingConfig &cfg) +void hollow_mesh(TriangleMesh &mesh, const HollowingConfig &cfg, int flags) { - std::unique_ptr inter_ptr = - Slic3r::sla::generate_interior(mesh); + InteriorPtr interior = generate_interior(mesh, cfg, JobController{}); + if (!interior) return; - if (inter_ptr) mesh.merge(*inter_ptr); + hollow_mesh(mesh, *interior, flags); +} + +void hollow_mesh(TriangleMesh &mesh, const Interior &interior, int flags) +{ + if (mesh.empty() || interior.mesh.empty()) return; + +// if (flags & hfRemoveInsideTriangles && interior.gridptr) +// erase_inside_triangles_2(mesh, interior); + + mesh.merge(interior.mesh); mesh.require_shared_vertices(); } diff --git a/src/libslic3r/SLA/Hollowing.hpp b/src/libslic3r/SLA/Hollowing.hpp index 949cc23935..356907846f 100644 --- a/src/libslic3r/SLA/Hollowing.hpp +++ b/src/libslic3r/SLA/Hollowing.hpp @@ -19,6 +19,17 @@ struct HollowingConfig bool enabled = true; }; +enum HollowingFlags { hfRemoveInsideTriangles = 0x1 }; + +// All data related to a generated mesh interior. Includes the 3D grid and mesh +// and various metadata. No need to manipulate from outside. +struct Interior; +struct InteriorDeleter { void operator()(Interior *p); }; +using InteriorPtr = std::unique_ptr; + +TriangleMesh & get_mesh(Interior &interior); +const TriangleMesh &get_mesh(const Interior &interior); + struct DrainHole { Vec3f pos; @@ -60,11 +71,15 @@ using DrainHoles = std::vector; constexpr float HoleStickOutLength = 1.f; -std::unique_ptr generate_interior(const TriangleMesh &mesh, - const HollowingConfig & = {}, - const JobController &ctl = {}); +InteriorPtr generate_interior(const TriangleMesh &mesh, + const HollowingConfig & = {}, + const JobController &ctl = {}); -void hollow_mesh(TriangleMesh &mesh, const HollowingConfig &cfg); +// Will do the hollowing +void hollow_mesh(TriangleMesh &mesh, const HollowingConfig &cfg, int flags = 0); + +// Hollowing prepared in "interior", merge with original mesh +void hollow_mesh(TriangleMesh &mesh, const Interior &interior, int flags = 0); void cut_drainholes(std::vector & obj_slices, const std::vector &slicegrid, diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 16b068cb90..07042692a0 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1149,8 +1149,9 @@ const TriangleMesh& SLAPrintObject::pad_mesh() const const TriangleMesh &SLAPrintObject::hollowed_interior_mesh() const { - if (m_hollowing_data && m_config.hollowing_enable.getBool()) - return m_hollowing_data->interior; + if (m_hollowing_data && m_hollowing_data->interior && + m_config.hollowing_enable.getBool()) + return sla::get_mesh(*m_hollowing_data->interior); return EMPTY_MESH; } diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index bed66ab4f4..87ab3db8ac 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -327,8 +327,8 @@ private: class HollowingData { public: - - TriangleMesh interior; + + sla::InteriorPtr interior; mutable TriangleMesh hollow_mesh_with_holes; // caching the complete hollowed mesh }; diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index d8bea62aea..976e6d3d1e 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -131,13 +131,14 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po) double quality = po.m_config.hollowing_quality.getFloat(); double closing_d = po.m_config.hollowing_closing_distance.getFloat(); sla::HollowingConfig hlwcfg{thickness, quality, closing_d}; - auto meshptr = generate_interior(po.transformed_mesh(), hlwcfg); - if (meshptr->empty()) + sla::InteriorPtr interior = generate_interior(po.transformed_mesh(), hlwcfg); + + if (!interior || sla::get_mesh(*interior).empty()) BOOST_LOG_TRIVIAL(warning) << "Hollowed interior is empty!"; else { po.m_hollowing_data.reset(new SLAPrintObject::HollowingData()); - po.m_hollowing_data->interior = *meshptr; + po.m_hollowing_data->interior = std::move(interior); } } @@ -145,7 +146,9 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po) void SLAPrint::Steps::drill_holes(SLAPrintObject &po) { bool needs_drilling = ! po.m_model_object->sla_drain_holes.empty(); - bool is_hollowed = (po.m_hollowing_data && ! po.m_hollowing_data->interior.empty()); + bool is_hollowed = + (po.m_hollowing_data && po.m_hollowing_data->interior && + !sla::get_mesh(*po.m_hollowing_data->interior).empty()); if (! is_hollowed && ! needs_drilling) { // In this case we can dump any data that might have been @@ -163,10 +166,7 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) // holes that are no longer on the frontend. TriangleMesh &hollowed_mesh = po.m_hollowing_data->hollow_mesh_with_holes; hollowed_mesh = po.transformed_mesh(); - if (! po.m_hollowing_data->interior.empty()) { - hollowed_mesh.merge(po.m_hollowing_data->interior); - hollowed_mesh.require_shared_vertices(); - } + sla::hollow_mesh(hollowed_mesh, *po.m_hollowing_data->interior/*, sla::hfRemoveInsideTriangles*/); if (! needs_drilling) { BOOST_LOG_TRIVIAL(info) << "Drilling skipped (no holes)."; @@ -260,9 +260,15 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po) auto &slice_grid = po.m_model_height_levels; slicer.slice(slice_grid, SlicingMode::Regular, closing_r, &po.m_model_slices, thr); - if (po.m_hollowing_data && ! po.m_hollowing_data->interior.empty()) { - po.m_hollowing_data->interior.repair(true); - TriangleMeshSlicer interior_slicer(&po.m_hollowing_data->interior); + sla::Interior *interior = po.m_hollowing_data ? + po.m_hollowing_data->interior.get() : + nullptr; + + if (interior && ! sla::get_mesh(*interior).empty()) { + TriangleMesh interiormesh = sla::get_mesh(*interior); + interiormesh.repaired = false; + interiormesh.repair(true); + TriangleMeshSlicer interior_slicer(&interiormesh); std::vector interior_slices; interior_slicer.slice(slice_grid, SlicingMode::Regular, closing_r, &interior_slices, thr); diff --git a/tests/libslic3r/test_hollowing.cpp b/tests/libslic3r/test_hollowing.cpp index 65b87c2a23..2218a27b75 100644 --- a/tests/libslic3r/test_hollowing.cpp +++ b/tests/libslic3r/test_hollowing.cpp @@ -26,21 +26,19 @@ static Slic3r::TriangleMesh load_model(const std::string &obj_filename) } -TEST_CASE("Negative 3D offset should produce smaller object.", "[Hollowing]") -{ - Slic3r::TriangleMesh in_mesh = load_model("20mm_cube.obj"); - Benchmark bench; - bench.start(); - - std::unique_ptr out_mesh_ptr = - Slic3r::sla::generate_interior(in_mesh); - - bench.stop(); - - std::cout << "Elapsed processing time: " << bench.getElapsedSec() << std::endl; - - if (out_mesh_ptr) in_mesh.merge(*out_mesh_ptr); - in_mesh.require_shared_vertices(); - in_mesh.WriteOBJFile("merged_out.obj"); +TEST_CASE("Hollow two overlapping spheres") { + using namespace Slic3r; + + TriangleMesh sphere1 = make_sphere(10., 2 * PI / 20.), sphere2 = sphere1; + + sphere1.translate(-5.f, 0.f, 0.f); + sphere2.translate( 5.f, 0.f, 0.f); + + sphere1.merge(sphere2); + sphere1.require_shared_vertices(); + + sla::hollow_mesh(sphere1, sla::HollowingConfig{}, sla::HollowingFlags::hfRemoveInsideTriangles); + + sphere1.WriteOBJFile("twospheres.obj"); } diff --git a/tests/sla_print/sla_test_utils.cpp b/tests/sla_print/sla_test_utils.cpp index 653221cd3f..1ec890beb3 100644 --- a/tests/sla_print/sla_test_utils.cpp +++ b/tests/sla_print/sla_test_utils.cpp @@ -88,9 +88,9 @@ void test_supports(const std::string &obj_filename, REQUIRE_FALSE(mesh.empty()); if (hollowingcfg.enabled) { - auto inside = sla::generate_interior(mesh, hollowingcfg); - REQUIRE(inside); - mesh.merge(*inside); + sla::InteriorPtr interior = sla::generate_interior(mesh, hollowingcfg); + REQUIRE(interior); + mesh.merge(sla::get_mesh(*interior)); mesh.require_shared_vertices(); } From 527e6752941bf8336fba91e63de65cf8fb4da544 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 4 Jan 2021 14:12:40 +0100 Subject: [PATCH 066/285] Use triangle removal only for visualized mesh --- src/libslic3r/SLA/Hollowing.cpp | 5 +++++ src/libslic3r/SLA/Hollowing.hpp | 2 ++ src/libslic3r/SLAPrint.hpp | 5 +++++ src/libslic3r/SLAPrintSteps.cpp | 9 ++++++--- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index 44358cebef..a350e6faa9 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -324,4 +324,9 @@ void hollow_mesh(TriangleMesh &mesh, const Interior &interior, int flags) mesh.require_shared_vertices(); } +void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior) +{ + +} + }} // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/Hollowing.hpp b/src/libslic3r/SLA/Hollowing.hpp index 356907846f..caa7d7b6ba 100644 --- a/src/libslic3r/SLA/Hollowing.hpp +++ b/src/libslic3r/SLA/Hollowing.hpp @@ -81,6 +81,8 @@ void hollow_mesh(TriangleMesh &mesh, const HollowingConfig &cfg, int flags = 0); // Hollowing prepared in "interior", merge with original mesh void hollow_mesh(TriangleMesh &mesh, const Interior &interior, int flags = 0); +void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior); + void cut_drainholes(std::vector & obj_slices, const std::vector &slicegrid, float closing_radius, diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 87ab3db8ac..74c71dc1e8 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -85,6 +85,10 @@ public: // Get the mesh that is going to be printed with all the modifications // like hollowing and drilled holes. const TriangleMesh & get_mesh_to_print() const { + return (m_hollowing_data && is_step_done(slaposDrillHoles)) ? m_hollowing_data->hollow_mesh_with_holes_trimmed : transformed_mesh(); + } + + const TriangleMesh & get_mesh_to_slice() const { return (m_hollowing_data && is_step_done(slaposDrillHoles)) ? m_hollowing_data->hollow_mesh_with_holes : transformed_mesh(); } @@ -330,6 +334,7 @@ private: sla::InteriorPtr interior; mutable TriangleMesh hollow_mesh_with_holes; // caching the complete hollowed mesh + mutable TriangleMesh hollow_mesh_with_holes_trimmed; }; std::unique_ptr m_hollowing_data; diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 976e6d3d1e..59ad20c721 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -166,7 +166,10 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) // holes that are no longer on the frontend. TriangleMesh &hollowed_mesh = po.m_hollowing_data->hollow_mesh_with_holes; hollowed_mesh = po.transformed_mesh(); - sla::hollow_mesh(hollowed_mesh, *po.m_hollowing_data->interior/*, sla::hfRemoveInsideTriangles*/); + sla::hollow_mesh(hollowed_mesh, *po.m_hollowing_data->interior); + + TriangleMesh &mesh_view = po.m_hollowing_data->hollow_mesh_with_holes_trimmed; + sla::remove_inside_triangles(mesh_view, *po.m_hollowing_data->interior); if (! needs_drilling) { BOOST_LOG_TRIVIAL(info) << "Drilling skipped (no holes)."; @@ -213,7 +216,7 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) // same imaginary grid (the height vector argument to TriangleMeshSlicer). void SLAPrint::Steps::slice_model(SLAPrintObject &po) { - const TriangleMesh &mesh = po.get_mesh_to_print(); + const TriangleMesh &mesh = po.get_mesh_to_slice(); // We need to prepare the slice index... @@ -303,7 +306,7 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po) // If supports are disabled, we can skip the model scan. if(!po.m_config.supports_enable.getBool()) return; - const TriangleMesh &mesh = po.get_mesh_to_print(); + const TriangleMesh &mesh = po.get_mesh_to_slice(); if (!po.m_supportdata) po.m_supportdata.reset(new SLAPrintObject::SupportData(mesh)); From d48ca7fd030119a926518c1c5705854471d4a5ed Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 2 Mar 2021 17:03:33 +0100 Subject: [PATCH 067/285] Fix incorrect mesh shown on plater after hollowing --- src/libslic3r/SLAPrint.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 07042692a0..42ed8b80fa 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1120,7 +1120,7 @@ TriangleMesh SLAPrintObject::get_mesh(SLAPrintObjectStep step) const return this->pad_mesh(); case slaposDrillHoles: if (m_hollowing_data) - return m_hollowing_data->hollow_mesh_with_holes; + return get_mesh_to_print(); [[fallthrough]]; default: return TriangleMesh(); From dd202af8cd3a96977acfefc26c898989129f6485 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 26 Feb 2021 15:37:26 +0100 Subject: [PATCH 068/285] Fix stl export with hollowed mesh --- src/slic3r/GUI/Plater.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 7a38dd3a1c..3d4d381fb8 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5363,7 +5363,7 @@ void Plater::export_stl(bool extended, bool selection_only) inst_mesh.merge(inst_supports_mesh); } - TriangleMesh inst_object_mesh = object->get_mesh_to_print(); + TriangleMesh inst_object_mesh = object->get_mesh_to_slice(); inst_object_mesh.transform(mesh_trafo_inv); inst_object_mesh.transform(inst_transform, is_left_handed); From 06bf02df695ee70f266a5c33c55f73f5f7714d1d Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 26 Feb 2021 15:37:48 +0100 Subject: [PATCH 069/285] Fix Gizmo preview with hollowed mesh --- src/libslic3r/SLAPrintSteps.cpp | 305 ++++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 18 +- src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 2 + src/slic3r/GUI/MeshUtils.cpp | 19 ++ src/slic3r/GUI/MeshUtils.hpp | 3 + 5 files changed, 194 insertions(+), 153 deletions(-) diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 59ad20c721..4637aa761e 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -84,17 +84,17 @@ SLAPrint::Steps::Steps(SLAPrint *print) void SLAPrint::Steps::apply_printer_corrections(SLAPrintObject &po, SliceOrigin o) { if (o == soSupport && !po.m_supportdata) return; - + auto faded_lyrs = size_t(po.m_config.faded_layers.getInt()); double min_w = m_print->m_printer_config.elefant_foot_min_width.getFloat() / 2.; double start_efc = m_print->m_printer_config.elefant_foot_compensation.getFloat(); - + double doffs = m_print->m_printer_config.absolute_correction.getFloat(); coord_t clpr_offs = scaled(doffs); - + faded_lyrs = std::min(po.m_slice_index.size(), faded_lyrs); size_t faded_lyrs_efc = std::max(size_t(1), faded_lyrs - 1); - + auto efc = [start_efc, faded_lyrs_efc](size_t pos) { return (faded_lyrs_efc - pos) * start_efc / faded_lyrs_efc; }; @@ -102,13 +102,13 @@ void SLAPrint::Steps::apply_printer_corrections(SLAPrintObject &po, SliceOrigin std::vector &slices = o == soModel ? po.m_model_slices : po.m_supportdata->support_slices; - + if (clpr_offs != 0) for (size_t i = 0; i < po.m_slice_index.size(); ++i) { size_t idx = po.m_slice_index[i].get_slice_idx(o); if (idx < slices.size()) slices[idx] = offset_ex(slices[idx], float(clpr_offs)); } - + if (start_efc > 0.) for (size_t i = 0; i < faded_lyrs; ++i) { size_t idx = po.m_slice_index[i].get_slice_idx(o); if (idx < slices.size()) @@ -124,7 +124,7 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po) BOOST_LOG_TRIVIAL(info) << "Skipping hollowing step!"; return; } - + BOOST_LOG_TRIVIAL(info) << "Performing hollowing step!"; double thickness = po.m_config.hollowing_min_thickness.getFloat(); @@ -169,16 +169,17 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) sla::hollow_mesh(hollowed_mesh, *po.m_hollowing_data->interior); TriangleMesh &mesh_view = po.m_hollowing_data->hollow_mesh_with_holes_trimmed; - sla::remove_inside_triangles(mesh_view, *po.m_hollowing_data->interior); + mesh_view = po.transformed_mesh(); + sla::hollow_mesh(mesh_view, *po.m_hollowing_data->interior, sla::hfRemoveInsideTriangles); if (! needs_drilling) { BOOST_LOG_TRIVIAL(info) << "Drilling skipped (no holes)."; return; } - + BOOST_LOG_TRIVIAL(info) << "Drilling drainage holes."; sla::DrainHoles drainholes = po.transformed_drainhole_points(); - + std::uniform_real_distribution dist(0., float(EPSILON)); auto holes_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal({}); for (sla::DrainHole holept : drainholes) { @@ -190,12 +191,12 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) auto cgal_m = MeshBoolean::cgal::triangle_mesh_to_cgal(m); MeshBoolean::cgal::plus(*holes_mesh_cgal, *cgal_m); } - + if (MeshBoolean::cgal::does_self_intersect(*holes_mesh_cgal)) throw Slic3r::SlicingError(L("Too many overlapping holes.")); - + auto hollowed_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal(hollowed_mesh); - + try { MeshBoolean::cgal::minus(*hollowed_mesh_cgal, *holes_mesh_cgal); hollowed_mesh = MeshBoolean::cgal::cgal_to_triangle_mesh(*hollowed_mesh_cgal); @@ -215,11 +216,11 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) // of it. In any case, the model and the supports have to be sliced in the // same imaginary grid (the height vector argument to TriangleMeshSlicer). void SLAPrint::Steps::slice_model(SLAPrintObject &po) -{ +{ const TriangleMesh &mesh = po.get_mesh_to_slice(); // We need to prepare the slice index... - + double lhd = m_print->m_objects.front()->m_config.layer_height.getFloat(); float lh = float(lhd); coord_t lhs = scaled(lhd); @@ -229,40 +230,40 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po) auto minZf = float(minZ); coord_t minZs = scaled(minZ); coord_t maxZs = scaled(maxZ); - + po.m_slice_index.clear(); - + size_t cap = size_t(1 + (maxZs - minZs - ilhs) / lhs); po.m_slice_index.reserve(cap); - + po.m_slice_index.emplace_back(minZs + ilhs, minZf + ilh / 2.f, ilh); - + for(coord_t h = minZs + ilhs + lhs; h <= maxZs; h += lhs) po.m_slice_index.emplace_back(h, unscaled(h) - lh / 2.f, lh); - + // Just get the first record that is from the model: auto slindex_it = po.closest_slice_record(po.m_slice_index, float(bb3d.min(Z))); - + if(slindex_it == po.m_slice_index.end()) //TRN To be shown at the status bar on SLA slicing error. throw Slic3r::RuntimeError( L("Slicing had to be stopped due to an internal error: " "Inconsistent slice index.")); - + po.m_model_height_levels.clear(); po.m_model_height_levels.reserve(po.m_slice_index.size()); for(auto it = slindex_it; it != po.m_slice_index.end(); ++it) po.m_model_height_levels.emplace_back(it->slice_level()); - + TriangleMeshSlicer slicer(&mesh); - + po.m_model_slices.clear(); float closing_r = float(po.config().slice_closing_radius.value); auto thr = [this]() { m_print->throw_if_canceled(); }; auto &slice_grid = po.m_model_height_levels; slicer.slice(slice_grid, SlicingMode::Regular, closing_r, &po.m_model_slices, thr); - + sla::Interior *interior = po.m_hollowing_data ? po.m_hollowing_data->interior.get() : nullptr; @@ -282,17 +283,17 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po) diff_ex(po.m_model_slices[i], slice); }); } - + auto mit = slindex_it; for (size_t id = 0; id < po.m_model_slices.size() && mit != po.m_slice_index.end(); id++) { mit->set_model_slice_idx(po, id); ++mit; } - + // We apply the printer correction offset here. apply_printer_corrections(po, soModel); - + if(po.m_config.supports_enable.getBool() || po.m_config.pad_enable.getBool()) { po.m_supportdata.reset(new SLAPrintObject::SupportData(mesh)); @@ -305,22 +306,22 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po) { // If supports are disabled, we can skip the model scan. if(!po.m_config.supports_enable.getBool()) return; - + const TriangleMesh &mesh = po.get_mesh_to_slice(); - + if (!po.m_supportdata) po.m_supportdata.reset(new SLAPrintObject::SupportData(mesh)); - + const ModelObject& mo = *po.m_model_object; - + BOOST_LOG_TRIVIAL(debug) << "Support point count " << mo.sla_support_points.size(); - + // Unless the user modified the points or we already did the calculation, // we will do the autoplacement. Otherwise we will just blindly copy the // frontend data into the backend cache. if (mo.sla_points_status != sla::PointsStatus::UserModified) { - + // calculate heights of slices (slices are calculated already) const std::vector& heights = po.m_model_height_levels; @@ -328,27 +329,27 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po) // calculated on slices, the algorithm then raycasts the points // so they actually lie on the mesh. // po.m_supportdata->emesh.load_holes(po.transformed_drainhole_points()); - + throw_if_canceled(); sla::SupportPointGenerator::Config config; const SLAPrintObjectConfig& cfg = po.config(); - + // the density config value is in percents: config.density_relative = float(cfg.support_points_density_relative / 100.f); config.minimal_distance = float(cfg.support_points_minimal_distance); config.head_diameter = float(cfg.support_head_front_diameter); - + // scaling for the sub operations double d = objectstep_scale * OBJ_STEP_LEVELS[slaposSupportPoints] / 100.0; double init = current_status(); - + auto statuscb = [this, d, init](unsigned st) { double current = init + st * d; if(std::round(current_status()) < std::round(current)) report_status(current, OBJ_STEP_LABELS(slaposSupportPoints)); }; - + // Construction of this object does the calculation. throw_if_canceled(); sla::SupportPointGenerator auto_supports( @@ -359,10 +360,10 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po) const std::vector& points = auto_supports.output(); throw_if_canceled(); po.m_supportdata->pts = points; - + BOOST_LOG_TRIVIAL(debug) << "Automatic support points: " << po.m_supportdata->pts.size(); - + // Using RELOAD_SLA_SUPPORT_POINTS to tell the Plater to pass // the update status to GLGizmoSlaSupports report_status(-1, L("Generating support points"), @@ -377,9 +378,9 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po) void SLAPrint::Steps::support_tree(SLAPrintObject &po) { if(!po.m_supportdata) return; - + sla::PadConfig pcfg = make_pad_cfg(po.m_config); - + if (pcfg.embed_object) po.m_supportdata->emesh.ground_level_offset(pcfg.wall_thickness_mm); @@ -389,15 +390,15 @@ void SLAPrint::Steps::support_tree(SLAPrintObject &po) remove_bottom_points(po.m_supportdata->pts, float(po.m_supportdata->emesh.ground_level() + EPSILON)); } - + po.m_supportdata->cfg = make_support_cfg(po.m_config); // po.m_supportdata->emesh.load_holes(po.transformed_drainhole_points()); - + // scaling for the sub operations double d = objectstep_scale * OBJ_STEP_LEVELS[slaposSupportTree] / 100.0; double init = current_status(); sla::JobController ctl; - + ctl.statuscb = [this, d, init](unsigned st, const std::string &logmsg) { double current = init + st * d; if (std::round(current_status()) < std::round(current)) @@ -406,26 +407,26 @@ void SLAPrint::Steps::support_tree(SLAPrintObject &po) }; ctl.stopcondition = [this]() { return canceled(); }; ctl.cancelfn = [this]() { throw_if_canceled(); }; - + po.m_supportdata->create_support_tree(ctl); - + if (!po.m_config.supports_enable.getBool()) return; - + throw_if_canceled(); - + // Create the unified mesh auto rc = SlicingStatus::RELOAD_SCENE; - + // This is to prevent "Done." being displayed during merged_mesh() report_status(-1, L("Visualizing supports")); - + BOOST_LOG_TRIVIAL(debug) << "Processed support point count " << po.m_supportdata->pts.size(); - + // Check the mesh for later troubleshooting. if(po.support_mesh().empty()) BOOST_LOG_TRIVIAL(warning) << "Support mesh is empty"; - + report_status(-1, L("Visualizing supports"), rc); } @@ -433,15 +434,15 @@ void SLAPrint::Steps::generate_pad(SLAPrintObject &po) { // this step can only go after the support tree has been created // and before the supports had been sliced. (or the slicing has to be // repeated) - + if(po.m_config.pad_enable.getBool()) { // Get the distilled pad configuration from the config sla::PadConfig pcfg = make_pad_cfg(po.m_config); - + ExPolygons bp; // This will store the base plate of the pad. double pad_h = pcfg.full_height(); const TriangleMesh &trmesh = po.transformed_mesh(); - + if (!po.m_config.supports_enable.getBool() || pcfg.embed_object) { // No support (thus no elevation) or zero elevation mode // we sometimes call it "builtin pad" is enabled so we will @@ -451,19 +452,19 @@ void SLAPrint::Steps::generate_pad(SLAPrintObject &po) { float(po.m_config.layer_height.getFloat()), [this](){ throw_if_canceled(); }); } - + po.m_supportdata->support_tree_ptr->add_pad(bp, pcfg); auto &pad_mesh = po.m_supportdata->support_tree_ptr->retrieve_mesh(sla::MeshType::Pad); - + if (!validate_pad(pad_mesh, pcfg)) throw Slic3r::SlicingError( L("No pad can be generated for this model with the " "current configuration")); - + } else if(po.m_supportdata && po.m_supportdata->support_tree_ptr) { po.m_supportdata->support_tree_ptr->remove_pad(); } - + throw_if_canceled(); report_status(-1, L("Visualizing supports"), SlicingStatus::RELOAD_SCENE); } @@ -473,25 +474,25 @@ void SLAPrint::Steps::generate_pad(SLAPrintObject &po) { // be part of the slices) void SLAPrint::Steps::slice_supports(SLAPrintObject &po) { auto& sd = po.m_supportdata; - + if(sd) sd->support_slices.clear(); - + // Don't bother if no supports and no pad is present. if (!po.m_config.supports_enable.getBool() && !po.m_config.pad_enable.getBool()) return; - + if(sd && sd->support_tree_ptr) { auto heights = reserve_vector(po.m_slice_index.size()); - + for(auto& rec : po.m_slice_index) heights.emplace_back(rec.slice_level()); sd->support_slices = sd->support_tree_ptr->slice( heights, float(po.config().slice_closing_radius.value)); } - - for (size_t i = 0; i < sd->support_slices.size() && i < po.m_slice_index.size(); ++i) + + for (size_t i = 0; i < sd->support_slices.size() && i < po.m_slice_index.size(); ++i) po.m_slice_index[i].set_support_slice_idx(po, i); - + apply_printer_corrections(po, soSupport); // Using RELOAD_SLA_PREVIEW to tell the Plater to pass the update @@ -506,37 +507,37 @@ using ClipperPolygons = std::vector; static ClipperPolygons polyunion(const ClipperPolygons &subjects) { ClipperLib::Clipper clipper; - + bool closed = true; - + for(auto& path : subjects) { clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed); clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed); } - + auto mode = ClipperLib::pftPositive; - + return libnest2d::clipper_execute(clipper, ClipperLib::ctUnion, mode, mode); } static ClipperPolygons polydiff(const ClipperPolygons &subjects, const ClipperPolygons& clips) { ClipperLib::Clipper clipper; - + bool closed = true; - + for(auto& path : subjects) { clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed); clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed); } - + for(auto& path : clips) { clipper.AddPath(path.Contour, ClipperLib::ptClip, closed); clipper.AddPaths(path.Holes, ClipperLib::ptClip, closed); } - + auto mode = ClipperLib::pftPositive; - + return libnest2d::clipper_execute(clipper, ClipperLib::ctDifference, mode, mode); } @@ -544,28 +545,28 @@ static ClipperPolygons polydiff(const ClipperPolygons &subjects, const ClipperPo static ClipperPolygons get_all_polygons(const SliceRecord& record, SliceOrigin o) { namespace sl = libnest2d::sl; - + if (!record.print_obj()) return {}; - + ClipperPolygons polygons; auto &input_polygons = record.get_slice(o); auto &instances = record.print_obj()->instances(); bool is_lefthanded = record.print_obj()->is_left_handed(); polygons.reserve(input_polygons.size() * instances.size()); - + for (const ExPolygon& polygon : input_polygons) { if(polygon.contour.empty()) continue; - + for (size_t i = 0; i < instances.size(); ++i) { ClipperPolygon poly; - + // We need to reverse if is_lefthanded is true but bool needreverse = is_lefthanded; - + // should be a move poly.Contour.reserve(polygon.contour.size() + 1); - + auto& cntr = polygon.contour.points; if(needreverse) for(auto it = cntr.rbegin(); it != cntr.rend(); ++it) @@ -573,12 +574,12 @@ static ClipperPolygons get_all_polygons(const SliceRecord& record, SliceOrigin o else for(auto& p : cntr) poly.Contour.emplace_back(p.x(), p.y()); - + for(auto& h : polygon.holes) { poly.Holes.emplace_back(); auto& hole = poly.Holes.back(); hole.reserve(h.points.size() + 1); - + if(needreverse) for(auto it = h.points.rbegin(); it != h.points.rend(); ++it) hole.emplace_back(it->x(), it->y()); @@ -586,42 +587,42 @@ static ClipperPolygons get_all_polygons(const SliceRecord& record, SliceOrigin o for(auto& p : h.points) hole.emplace_back(p.x(), p.y()); } - + if(is_lefthanded) { for(auto& p : poly.Contour) p.X = -p.X; for(auto& h : poly.Holes) for(auto& p : h) p.X = -p.X; } - + sl::rotate(poly, double(instances[i].rotation)); sl::translate(poly, ClipperPoint{instances[i].shift.x(), instances[i].shift.y()}); - + polygons.emplace_back(std::move(poly)); } } - + return polygons; } void SLAPrint::Steps::initialize_printer_input() { auto &printer_input = m_print->m_printer_input; - + // clear the rasterizer input printer_input.clear(); - + size_t mx = 0; for(SLAPrintObject * o : m_print->m_objects) { if(auto m = o->get_slice_index().size() > mx) mx = m; } - + printer_input.reserve(mx); - + auto eps = coord_t(SCALED_EPSILON); - + for(SLAPrintObject * o : m_print->m_objects) { coord_t gndlvl = o->get_slice_index().front().print_level() - ilhs; - + for(const SliceRecord& slicerecord : o->get_slice_index()) { if (!slicerecord.is_valid()) throw Slic3r::SlicingError( @@ -630,7 +631,7 @@ void SLAPrint::Steps::initialize_printer_input() "objects printable.")); coord_t lvlid = slicerecord.print_level() - gndlvl; - + // Neat trick to round the layer levels to the grid. lvlid = eps * (lvlid / eps); @@ -640,8 +641,8 @@ void SLAPrint::Steps::initialize_printer_input() if(it == printer_input.end() || it->level() != lvlid) it = printer_input.insert(it, PrintLayer(lvlid)); - - + + it->add(slicerecord); } } @@ -650,53 +651,53 @@ void SLAPrint::Steps::initialize_printer_input() // Merging the slices from all the print objects into one slice grid and // calculating print statistics from the merge result. void SLAPrint::Steps::merge_slices_and_eval_stats() { - + initialize_printer_input(); - + auto &print_statistics = m_print->m_print_statistics; auto &printer_config = m_print->m_printer_config; auto &material_config = m_print->m_material_config; auto &printer_input = m_print->m_printer_input; - + print_statistics.clear(); - + // libnest calculates positive area for clockwise polygons, Slic3r is in counter-clockwise auto areafn = [](const ClipperPolygon& poly) { return - libnest2d::sl::area(poly); }; - + const double area_fill = printer_config.area_fill.getFloat()*0.01;// 0.5 (50%); const double fast_tilt = printer_config.fast_tilt_time.getFloat();// 5.0; const double slow_tilt = printer_config.slow_tilt_time.getFloat();// 8.0; - + const double init_exp_time = material_config.initial_exposure_time.getFloat(); const double exp_time = material_config.exposure_time.getFloat(); - + const int fade_layers_cnt = m_print->m_default_object_config.faded_layers.getInt();// 10 // [3;20] - + const auto width = scaled(printer_config.display_width.getFloat()); const auto height = scaled(printer_config.display_height.getFloat()); const double display_area = width*height; - + double supports_volume(0.0); double models_volume(0.0); - + double estim_time(0.0); std::vector layers_times; layers_times.reserve(printer_input.size()); - + size_t slow_layers = 0; size_t fast_layers = 0; - + const double delta_fade_time = (init_exp_time - exp_time) / (fade_layers_cnt + 1); double fade_layer_time = init_exp_time; - + sla::ccr::SpinningMutex mutex; using Lock = std::lock_guard; - + // Going to parallel: auto printlayerfn = [this, // functions and read only vars areafn, area_fill, display_area, exp_time, init_exp_time, fast_tilt, slow_tilt, delta_fade_time, - + // write vars &mutex, &models_volume, &supports_volume, &estim_time, &slow_layers, &fast_layers, &fade_layer_time, &layers_times](size_t sliced_layer_cnt) @@ -705,87 +706,87 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() { // vector of slice record references auto& slicerecord_references = layer.slices(); - + if(slicerecord_references.empty()) return; - + // Layer height should match for all object slices for a given level. const auto l_height = double(slicerecord_references.front().get().layer_height()); - + // Calculation of the consumed material - + ClipperPolygons model_polygons; ClipperPolygons supports_polygons; - + size_t c = std::accumulate(layer.slices().begin(), layer.slices().end(), size_t(0), [](size_t a, const SliceRecord &sr) { return a + sr.get_slice(soModel).size(); }); - + model_polygons.reserve(c); - + c = std::accumulate(layer.slices().begin(), layer.slices().end(), size_t(0), [](size_t a, const SliceRecord &sr) { return a + sr.get_slice(soModel).size(); }); - + supports_polygons.reserve(c); - + for(const SliceRecord& record : layer.slices()) { - + ClipperPolygons modelslices = get_all_polygons(record, soModel); for(ClipperPolygon& p_tmp : modelslices) model_polygons.emplace_back(std::move(p_tmp)); - + ClipperPolygons supportslices = get_all_polygons(record, soSupport); for(ClipperPolygon& p_tmp : supportslices) supports_polygons.emplace_back(std::move(p_tmp)); - + } - + model_polygons = polyunion(model_polygons); double layer_model_area = 0; for (const ClipperPolygon& polygon : model_polygons) layer_model_area += areafn(polygon); - + if (layer_model_area < 0 || layer_model_area > 0) { Lock lck(mutex); models_volume += layer_model_area * l_height; } - + if(!supports_polygons.empty()) { if(model_polygons.empty()) supports_polygons = polyunion(supports_polygons); else supports_polygons = polydiff(supports_polygons, model_polygons); // allegedly, union of subject is done withing the diff according to the pftPositive polyFillType } - + double layer_support_area = 0; for (const ClipperPolygon& polygon : supports_polygons) layer_support_area += areafn(polygon); - + if (layer_support_area < 0 || layer_support_area > 0) { Lock lck(mutex); supports_volume += layer_support_area * l_height; } - + // Here we can save the expensively calculated polygons for printing ClipperPolygons trslices; trslices.reserve(model_polygons.size() + supports_polygons.size()); for(ClipperPolygon& poly : model_polygons) trslices.emplace_back(std::move(poly)); for(ClipperPolygon& poly : supports_polygons) trslices.emplace_back(std::move(poly)); - + layer.transformed_slices(polyunion(trslices)); - + // Calculation of the slow and fast layers to the future controlling those values on FW - + const bool is_fast_layer = (layer_model_area + layer_support_area) <= display_area*area_fill; const double tilt_time = is_fast_layer ? fast_tilt : slow_tilt; - + { Lock lck(mutex); if (is_fast_layer) fast_layers++; else slow_layers++; - + // Calculation of the printing time double layer_times = 0.0; @@ -803,15 +804,15 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() { estim_time += layer_times; } }; - + // sequential version for debugging: // for(size_t i = 0; i < m_printer_input.size(); ++i) printlayerfn(i); sla::ccr::for_each(size_t(0), printer_input.size(), printlayerfn); - + auto SCALING2 = SCALING_FACTOR * SCALING_FACTOR; print_statistics.support_used_material = supports_volume * SCALING2; print_statistics.objects_used_material = models_volume * SCALING2; - + // Estimated printing time // A layers count o the highest object if (printer_input.size() == 0) @@ -820,10 +821,10 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() { print_statistics.estimated_print_time = estim_time; print_statistics.layers_times = layers_times; } - + print_statistics.fast_layers_count = fast_layers; print_statistics.slow_layers_count = slow_layers; - + report_status(-2, "", SlicingStatus::RELOAD_SLA_PREVIEW); } @@ -831,23 +832,23 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() { void SLAPrint::Steps::rasterize() { if(canceled() || !m_print->m_printer) return; - + // coefficient to map the rasterization state (0-99) to the allocated // portion (slot) of the process state double sd = (100 - max_objstatus) / 100.0; - + // slot is the portion of 100% that is realted to rasterization unsigned slot = PRINT_STEP_LEVELS[slapsRasterize]; - + // pst: previous state double pst = current_status(); - + double increment = (slot * sd) / m_print->m_printer_input.size(); double dstatus = current_status(); - + sla::ccr::SpinningMutex slck; using Lock = std::lock_guard; - + // procedure to process one height level. This will run in parallel auto lvlfn = [this, &slck, increment, &dstatus, &pst] @@ -855,10 +856,10 @@ void SLAPrint::Steps::rasterize() { PrintLayer& printlayer = m_print->m_printer_input[idx]; if(canceled()) return; - + for (const ClipperLib::Polygon& poly : printlayer.transformed_slices()) raster.draw(poly); - + // Status indication guarded with the spinlock { Lock lck(slck); @@ -870,10 +871,10 @@ void SLAPrint::Steps::rasterize() } } }; - + // last minute escape if(canceled()) return; - + // Print all the layers in parallel m_print->m_printer->draw_layers(m_print->m_printer_input.size(), lvlfn); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index a34c7562e7..7f6b106702 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -200,12 +200,20 @@ void HollowedMesh::on_update() if (print_object->is_step_done(slaposDrillHoles) && print_object->has_mesh(slaposDrillHoles)) { size_t timestamp = print_object->step_state_with_timestamp(slaposDrillHoles).timestamp; if (timestamp > m_old_hollowing_timestamp) { - const TriangleMesh& backend_mesh = print_object->get_mesh_to_print(); + const TriangleMesh& backend_mesh = print_object->get_mesh_to_slice(); if (! backend_mesh.empty()) { m_hollowed_mesh_transformed.reset(new TriangleMesh(backend_mesh)); Transform3d trafo_inv = canvas->sla_print()->sla_trafo(*mo).inverse(); m_hollowed_mesh_transformed->transform(trafo_inv); m_old_hollowing_timestamp = timestamp; + + const TriangleMesh &interior = print_object->hollowed_interior_mesh(); + if (!interior.empty()) { + m_hollowed_interior_transformed = std::make_unique(interior); + m_hollowed_interior_transformed->repaired = false; + m_hollowed_interior_transformed->repair(true); + m_hollowed_interior_transformed->transform(trafo_inv); + } } else m_hollowed_mesh_transformed.reset(nullptr); @@ -230,6 +238,10 @@ const TriangleMesh* HollowedMesh::get_hollowed_mesh() const return m_hollowed_mesh_transformed.get(); } +const TriangleMesh* HollowedMesh::get_hollowed_interior() const +{ + return m_hollowed_interior_transformed.get(); +} @@ -306,6 +318,10 @@ void ObjectClipper::on_update() m_clippers.back()->set_mesh(*mesh); } m_old_meshes = meshes; + + if (has_hollowed) + m_clippers.front()->set_negative_mesh(*get_pool()->hollowed_mesh()->get_hollowed_interior()); + m_active_inst_bb_radius = mo->instance_bounding_box(get_pool()->selection_info()->get_active_instance()).radius(); //if (has_hollowed && m_clp_ratio != 0.) diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index 61c2732976..ace2567487 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -199,6 +199,7 @@ public: #endif // NDEBUG const TriangleMesh* get_hollowed_mesh() const; + const TriangleMesh* get_hollowed_interior() const; protected: void on_update() override; @@ -206,6 +207,7 @@ protected: private: std::unique_ptr m_hollowed_mesh_transformed; + std::unique_ptr m_hollowed_interior_transformed; size_t m_old_hollowing_timestamp = 0; int m_print_object_idx = -1; int m_print_objects_count = 0; diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index ee0abe76f9..f9ccfd0d6e 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -2,6 +2,7 @@ #include "libslic3r/Tesselate.hpp" #include "libslic3r/TriangleMesh.hpp" +#include "libslic3r/ClipperUtils.hpp" #include "slic3r/GUI/Camera.hpp" @@ -31,6 +32,15 @@ void MeshClipper::set_mesh(const TriangleMesh& mesh) } } +void MeshClipper::set_negative_mesh(const TriangleMesh& mesh) +{ + if (m_negative_mesh != &mesh) { + m_negative_mesh = &mesh; + m_triangles_valid = false; + m_triangles2d.resize(0); + } +} + void MeshClipper::set_transformation(const Geometry::Transformation& trafo) @@ -74,6 +84,15 @@ void MeshClipper::recalculate_triangles() std::vector list_of_expolys; m_tms->set_up_direction(up.cast()); m_tms->slice(std::vector{height_mesh}, SlicingMode::Regular, 0.f, &list_of_expolys, [](){}); + + if (m_negative_mesh && !m_negative_mesh->empty()) { + TriangleMeshSlicer negative_tms{m_negative_mesh}; + negative_tms.set_up_direction(up.cast()); + + std::vector neg_polys; + negative_tms.slice(std::vector{height_mesh}, SlicingMode::Regular, 0.f, &neg_polys, [](){}); + list_of_expolys.front() = diff_ex(list_of_expolys.front(), neg_polys.front()); + } m_triangles2d = triangulate_expolygons_2f(list_of_expolys[0], m_trafo.get_matrix().matrix().determinant() < 0.); // Rotate the cut into world coords: diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index 60dcb30c81..09caf199bb 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -78,6 +78,8 @@ public: // must make sure that it stays valid. void set_mesh(const TriangleMesh& mesh); + void set_negative_mesh(const TriangleMesh &mesh); + // Inform the MeshClipper about the transformation that transforms the mesh // into world coordinates. void set_transformation(const Geometry::Transformation& trafo); @@ -91,6 +93,7 @@ private: Geometry::Transformation m_trafo; const TriangleMesh* m_mesh = nullptr; + const TriangleMesh* m_negative_mesh = nullptr; ClippingPlane m_plane; std::vector m_triangles2d; GLIndexedVertexArray m_vertex_array; From 195b39bb5bc47b07828ca2c1d0de8dd2107c1c4b Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 24 Feb 2021 18:56:01 +0100 Subject: [PATCH 070/285] Eliminate memory leaks from hollowing code --- src/libslic3r/OpenVDBUtils.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/OpenVDBUtils.cpp b/src/libslic3r/OpenVDBUtils.cpp index 259cd1cbdc..e09fceed7a 100644 --- a/src/libslic3r/OpenVDBUtils.cpp +++ b/src/libslic3r/OpenVDBUtils.cpp @@ -54,18 +54,20 @@ openvdb::FloatGrid::Ptr mesh_to_grid(const TriangleMesh & mesh, { openvdb::initialize(); - TriangleMeshPtrs meshparts = mesh.split(); + TriangleMeshPtrs meshparts_raw = mesh.split(); + auto meshparts = reserve_vector>(meshparts_raw.size()); + for (auto *p : meshparts_raw) + meshparts.emplace_back(p); - auto it = std::remove_if(meshparts.begin(), meshparts.end(), - [](TriangleMesh *m){ - m->require_shared_vertices(); - return !m->is_manifold() || m->volume() < EPSILON; - }); + auto it = std::remove_if(meshparts.begin(), meshparts.end(), [](auto &m) { + m->require_shared_vertices(); + return m->volume() < EPSILON; + }); meshparts.erase(it, meshparts.end()); openvdb::FloatGrid::Ptr grid; - for (TriangleMesh *m : meshparts) { + for (auto &m : meshparts) { auto subgrid = openvdb::tools::meshToVolume( TriangleMeshDataAdapter{*m, voxel_scale}, tr, exteriorBandWidth, interiorBandWidth, flags); From 7830c8f8aa2d8a447b0848771728575ba42c8618 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 4 Jan 2021 15:41:58 +0100 Subject: [PATCH 071/285] Add BoundingBox constructor with point set iterators --- src/libslic3r/BoundingBox.hpp | 52 ++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/BoundingBox.hpp b/src/libslic3r/BoundingBox.hpp index 8de28af5cd..37483fc3e2 100644 --- a/src/libslic3r/BoundingBox.hpp +++ b/src/libslic3r/BoundingBox.hpp @@ -21,22 +21,30 @@ public: min(pmin), max(pmax), defined(pmin(0) < pmax(0) && pmin(1) < pmax(1)) {} BoundingBoxBase(const PointClass &p1, const PointClass &p2, const PointClass &p3) : min(p1), max(p1), defined(false) { merge(p2); merge(p3); } - BoundingBoxBase(const std::vector& points) : min(PointClass::Zero()), max(PointClass::Zero()) + + template > + BoundingBoxBase(It from, It to) : min(PointClass::Zero()), max(PointClass::Zero()) { - if (points.empty()) { + if (from == to) { this->defined = false; // throw Slic3r::InvalidArgument("Empty point set supplied to BoundingBoxBase constructor"); } else { - typename std::vector::const_iterator it = points.begin(); - this->min = *it; - this->max = *it; - for (++ it; it != points.end(); ++ it) { - this->min = this->min.cwiseMin(*it); - this->max = this->max.cwiseMax(*it); + auto it = from; + this->min = it->template cast(); + this->max = this->min; + for (++ it; it != to; ++ it) { + auto vec = it->template cast(); + this->min = this->min.cwiseMin(vec); + this->max = this->max.cwiseMax(vec); } this->defined = (this->min(0) < this->max(0)) && (this->min(1) < this->max(1)); } } + + BoundingBoxBase(const std::vector &points) + : BoundingBoxBase(points.begin(), points.end()) + {} + void reset() { this->defined = false; this->min = PointClass::Zero(); this->max = PointClass::Zero(); } void merge(const PointClass &point); void merge(const std::vector &points); @@ -74,19 +82,27 @@ public: { if (pmin(2) >= pmax(2)) BoundingBoxBase::defined = false; } BoundingBox3Base(const PointClass &p1, const PointClass &p2, const PointClass &p3) : BoundingBoxBase(p1, p1) { merge(p2); merge(p3); } - BoundingBox3Base(const std::vector& points) + + template > BoundingBox3Base(It from, It to) { - if (points.empty()) + if (from == to) throw Slic3r::InvalidArgument("Empty point set supplied to BoundingBox3Base constructor"); - typename std::vector::const_iterator it = points.begin(); - this->min = *it; - this->max = *it; - for (++ it; it != points.end(); ++ it) { - this->min = this->min.cwiseMin(*it); - this->max = this->max.cwiseMax(*it); + + auto it = from; + this->min = it->template cast(); + this->max = this->min; + for (++ it; it != to; ++ it) { + auto vec = it->template cast(); + this->min = this->min.cwiseMin(vec); + this->max = this->max.cwiseMax(vec); } this->defined = (this->min(0) < this->max(0)) && (this->min(1) < this->max(1)) && (this->min(2) < this->max(2)); } + + BoundingBox3Base(const std::vector &points) + : BoundingBox3Base(points.begin(), points.end()) + {} + void merge(const PointClass &point); void merge(const std::vector &points); void merge(const BoundingBox3Base &bb); @@ -188,9 +204,7 @@ public: class BoundingBoxf3 : public BoundingBox3Base { public: - BoundingBoxf3() : BoundingBox3Base() {} - BoundingBoxf3(const Vec3d &pmin, const Vec3d &pmax) : BoundingBox3Base(pmin, pmax) {} - BoundingBoxf3(const std::vector &points) : BoundingBox3Base(points) {} + using BoundingBox3Base::BoundingBox3Base; BoundingBoxf3 transformed(const Transform3d& matrix) const; }; From e3c2e513fa599a31480979b535e4cfe9346d5806 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 25 Feb 2021 15:49:50 +0100 Subject: [PATCH 072/285] Do grid redistance even with zero closing distance This prevents having a leftover grid with zero at the exterior boundary. Trimming expects zero at (offset + closing distance) inwards --- src/libslic3r/SLA/Hollowing.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index a350e6faa9..ccc0caf98f 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -79,11 +79,7 @@ static InteriorPtr generate_interior_verbose(const TriangleMesh & mesh, double iso_surface = D; auto narrowb = double(in_range); - if (closing_dist > .0) { - gridptr = redistance_grid(*gridptr, -(offset + D), narrowb, narrowb); - } else { - iso_surface = -offset; - } + gridptr = redistance_grid(*gridptr, -(offset + D), narrowb, narrowb); if (ctl.stopcondition()) return {}; else ctl.statuscb(70, L("Hollowing")); From b8c1c136667c8d4ab679a571478fa653220a0b1f Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 2 Mar 2021 18:20:11 +0100 Subject: [PATCH 073/285] Add max_concurrency method for various execution policies --- src/libslic3r/SLA/Concurrency.hpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/SLA/Concurrency.hpp b/src/libslic3r/SLA/Concurrency.hpp index b692914aca..8ff0ff809e 100644 --- a/src/libslic3r/SLA/Concurrency.hpp +++ b/src/libslic3r/SLA/Concurrency.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -76,13 +77,18 @@ template<> struct _ccr from, to, init, std::forward(mergefn), [](typename I::value_type &i) { return i; }, granularity); } + + static size_t max_concurreny() + { + return tbb::this_task_arena::max_concurrency(); + } }; template<> struct _ccr { private: struct _Mtx { inline void lock() {} inline void unlock() {} }; - + public: using SpinningMutex = _Mtx; using BlockingMutex = _Mtx; @@ -133,6 +139,8 @@ public: return reduce(from, to, init, std::forward(mergefn), [](typename I::value_type &i) { return i; }); } + + static size_t max_concurreny() { return 1; } }; using ccr = _ccr; From 1ec154012ec8e1017a71f883356314894acd0b66 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 2 Mar 2021 18:24:57 +0100 Subject: [PATCH 074/285] Add working version of triangle trimming for hollowed meshes --- src/libslic3r/SLA/Hollowing.cpp | 245 +++++++++++++++++++++++++++++++- 1 file changed, 241 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index ccc0caf98f..6329172669 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -29,11 +29,21 @@ inline void _scale(S s, Contour3D &m) { for (auto &p : m.points) p *= s; } struct Interior { TriangleMesh mesh; openvdb::FloatGrid::Ptr gridptr; + mutable std::optional accessor; + double closing_distance = 0.; double thickness = 0.; double voxel_scale = 1.; - double nb_in = 3.; - double nb_out = 3.; + double nb_in = 3.; // narrow band width inwards + double nb_out = 3.; // narrow band width outwards + // Full narrow band is the sum of the two above values. + + void reset_accessor() const // This resets the accessor and its cache + // Not a thread safe call! + { + if (gridptr) + accessor = gridptr->getConstAccessor(); + } }; void InteriorDeleter::operator()(Interior *p) @@ -313,16 +323,243 @@ void hollow_mesh(TriangleMesh &mesh, const Interior &interior, int flags) { if (mesh.empty() || interior.mesh.empty()) return; -// if (flags & hfRemoveInsideTriangles && interior.gridptr) -// erase_inside_triangles_2(mesh, interior); + if (flags & hfRemoveInsideTriangles && interior.gridptr) + remove_inside_triangles(mesh, interior); mesh.merge(interior.mesh); mesh.require_shared_vertices(); } +// Get the distance of p to the interior's zero iso_surface. Interior should +// have its zero isosurface positioned at offset + closing_distance inwards form +// the model surface. +static double get_distance_raw(const Vec3f &p, const Interior &interior) +{ + assert(interior.gridptr); + + if (!interior.accessor) interior.reset_accessor(); + + auto v = (p * interior.voxel_scale).cast(); + auto grididx = interior.gridptr->transform().worldToIndexCellCentered( + {v.x(), v.y(), v.z()}); + + return interior.accessor->getValue(grididx) ; +} + +struct TriangleBubble { Vec3f center; double R; }; + +// Return the distance of bubble center to the interior boundary or NaN if the +// triangle is too big to be measured. +static double get_distance(const TriangleBubble &b, const Interior &interior) +{ + double R = b.R * interior.voxel_scale; + double D = get_distance_raw(b.center, interior); + + return (D > 0. && R >= interior.nb_out) || + (D < 0. && R >= interior.nb_in) || + ((D - R) < 0. && 2 * R > interior.thickness) ? + std::nan("") : + // FIXME: Adding interior.voxel_scale is a compromise supposed + // to prevent the deletion of the triangles forming the interior + // itself. This has a side effect that a small portion of the + // bad triangles will still be visible. + D - interior.closing_distance /*+ 2 * interior.voxel_scale*/; +} + +double get_distance(const Vec3f &p, const Interior &interior) +{ + double d = get_distance_raw(p, interior) - interior.closing_distance; + return d / interior.voxel_scale; +} + +// A face that can be divided. Stores the indices into the original mesh if its +// part of that mesh and the vertices it consists of. +enum { NEW_FACE = -1}; +struct DivFace { + Vec3i indx; + std::array verts; + long faceid = NEW_FACE; + long parent = NEW_FACE; +}; + +// Divide a face recursively and call visitor on all the sub-faces. +template +void divide_triangle(const DivFace &face, Fn &&visitor) +{ + std::array edges = {(face.verts[0] - face.verts[1]), + (face.verts[1] - face.verts[2]), + (face.verts[2] - face.verts[0])}; + + std::array edgeidx = {0, 1, 2}; + + std::sort(edgeidx.begin(), edgeidx.end(), [&edges](size_t e1, size_t e2) { + return edges[e1].squaredNorm() > edges[e2].squaredNorm(); + }); + + DivFace child1, child2; + + child1.parent = face.faceid == NEW_FACE ? face.parent : face.faceid; + child1.indx(0) = -1; + child1.indx(1) = face.indx(edgeidx[1]); + child1.indx(2) = face.indx((edgeidx[1] + 1) % 3); + child1.verts[0] = (face.verts[edgeidx[0]] + face.verts[(edgeidx[0] + 1) % 3]) / 2.; + child1.verts[1] = face.verts[edgeidx[1]]; + child1.verts[2] = face.verts[(edgeidx[1] + 1) % 3]; + + if (visitor(child1)) + divide_triangle(child1, std::forward(visitor)); + + child2.parent = face.faceid == NEW_FACE ? face.parent : face.faceid; + child2.indx(0) = -1; + child2.indx(1) = face.indx(edgeidx[2]); + child2.indx(2) = face.indx((edgeidx[2] + 1) % 3); + child2.verts[0] = child1.verts[0]; + child2.verts[1] = face.verts[edgeidx[2]]; + child2.verts[2] = face.verts[(edgeidx[2] + 1) % 3]; + + if (visitor(child2)) + divide_triangle(child2, std::forward(visitor)); +} + void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior) { + enum TrPos { posInside, posTouch, posOutside }; + auto &faces = mesh.its.indices; + auto &vertices = mesh.its.vertices; + auto bb = mesh.bounding_box(); + + // TODO: Parallel mode not working yet + using exec_policy = ccr_seq; + + // Info about the needed modifications on the input mesh. + struct MeshMods { + + // Just a thread safe wrapper for a vector of triangles. + struct { + std::vector> data; + exec_policy::SpinningMutex mutex; + + void emplace_back(const std::array &pts) + { + std::lock_guard lk{mutex}; + data.emplace_back(pts); + } + + size_t size() const { return data.size(); } + const std::array& operator[](size_t idx) const + { + return data[idx]; + } + + } new_triangles; + + // A vector of bool for all faces signaling if it needs to be removed + // or not. + std::vector to_remove; + + MeshMods(const TriangleMesh &mesh): + to_remove(mesh.its.indices.size(), false) {} + + // Number of triangles that need to be removed. + size_t to_remove_cnt() const + { + return std::accumulate(to_remove.begin(), to_remove.end(), size_t(0)); + } + + } mesh_mods{mesh}; + + // Must return true if further division of the face is needed. + auto divfn = [&interior, bb, &mesh_mods](const DivFace &f) { + BoundingBoxf3 facebb { f.verts.begin(), f.verts.end() }; + + // Face is certainly outside the cavity + if (! facebb.intersects(bb) && f.faceid != NEW_FACE) { + return false; + } + + TriangleBubble bubble{facebb.center().cast(), facebb.radius()}; + + double D = get_distance(bubble, interior); + double R = bubble.R * interior.voxel_scale; + + if (std::isnan(D)) // The distance cannot be measured, triangle too big + return true; + + // Distance of the bubble wall to the interior wall. Negative if the + // bubble is overlapping with the interior + double bubble_distance = D - R; + + // The face is crossing the interior or inside, it must be removed and + // parts of it re-added, that are outside the interior + if (bubble_distance < 0.) { + if (f.faceid != NEW_FACE) + mesh_mods.to_remove[f.faceid] = true; + + if (f.parent != NEW_FACE) // Top parent needs to be removed as well + mesh_mods.to_remove[f.parent] = true; + + // If the outside part is between the interior end the exterior + // (inside the wall being invisible), no further division is needed. + if ((R + D) < interior.thickness) + return false; + + return true; + } else if (f.faceid == NEW_FACE) { + // New face completely outside needs to be re-added. + mesh_mods.new_triangles.emplace_back(f.verts); + } + + return false; + }; + + interior.reset_accessor(); + + exec_policy::for_each(size_t(0), faces.size(), [&] (size_t face_idx) { + const Vec3i &face = faces[face_idx]; + + std::array pts = + { vertices[face(0)], vertices[face(1)], vertices[face(2)] }; + + BoundingBoxf3 facebb { pts.begin(), pts.end() }; + + // Face is certainly outside the cavity + if (! facebb.intersects(bb)) return; + + DivFace df{face, pts, long(face_idx)}; + + if (divfn(df)) + divide_triangle(df, divfn); + + }, exec_policy::max_concurreny()); + + auto new_faces = reserve_vector(faces.size() + + mesh_mods.new_triangles.size()); + + for (size_t face_idx = 0; face_idx < faces.size(); ++face_idx) { + if (!mesh_mods.to_remove[face_idx]) + new_faces.emplace_back(faces[face_idx]); + } + + for(size_t i = 0; i < mesh_mods.new_triangles.size(); ++i) { + size_t o = vertices.size(); + vertices.emplace_back(mesh_mods.new_triangles[i][0]); + vertices.emplace_back(mesh_mods.new_triangles[i][1]); + vertices.emplace_back(mesh_mods.new_triangles[i][2]); + new_faces.emplace_back(int(o), int(o + 1), int(o + 2)); + } + + BOOST_LOG_TRIVIAL(info) + << "Trimming: " << mesh_mods.to_remove_cnt() << " triangles removed"; + BOOST_LOG_TRIVIAL(info) + << "Trimming: " << mesh_mods.new_triangles.size() << " triangles added"; + + faces.swap(new_faces); + new_faces = {}; + + mesh = TriangleMesh{mesh.its}; + mesh.repaired = true; + mesh.require_shared_vertices(); } }} // namespace Slic3r::sla From 4374716bfb45fc1dfa0af37bbc5836dce4da012a Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 2 Mar 2021 18:25:24 +0100 Subject: [PATCH 075/285] Triangle trimming should handle drilled meshes separately --- src/libslic3r/SLAPrintSteps.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 4637aa761e..036fed171c 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -169,10 +169,10 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) sla::hollow_mesh(hollowed_mesh, *po.m_hollowing_data->interior); TriangleMesh &mesh_view = po.m_hollowing_data->hollow_mesh_with_holes_trimmed; - mesh_view = po.transformed_mesh(); - sla::hollow_mesh(mesh_view, *po.m_hollowing_data->interior, sla::hfRemoveInsideTriangles); if (! needs_drilling) { + mesh_view = po.transformed_mesh(); + sla::hollow_mesh(mesh_view, *po.m_hollowing_data->interior, sla::hfRemoveInsideTriangles); BOOST_LOG_TRIVIAL(info) << "Drilling skipped (no holes)."; return; } @@ -200,6 +200,9 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) try { MeshBoolean::cgal::minus(*hollowed_mesh_cgal, *holes_mesh_cgal); hollowed_mesh = MeshBoolean::cgal::cgal_to_triangle_mesh(*hollowed_mesh_cgal); + + mesh_view = hollowed_mesh; + sla::remove_inside_triangles(mesh_view, *po.m_hollowing_data->interior); } catch (const std::runtime_error &) { throw Slic3r::SlicingError(L( "Drilling holes into the mesh failed. " From fbc758642bbfc8f5affc0dff24ce10cc6b383b2d Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 2 Mar 2021 18:48:24 +0100 Subject: [PATCH 076/285] Fix crash when the interior is corrupted --- src/libslic3r/SLAPrintSteps.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 036fed171c..fe5d7505f9 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -166,13 +166,18 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) // holes that are no longer on the frontend. TriangleMesh &hollowed_mesh = po.m_hollowing_data->hollow_mesh_with_holes; hollowed_mesh = po.transformed_mesh(); - sla::hollow_mesh(hollowed_mesh, *po.m_hollowing_data->interior); + if (is_hollowed) + sla::hollow_mesh(hollowed_mesh, *po.m_hollowing_data->interior); TriangleMesh &mesh_view = po.m_hollowing_data->hollow_mesh_with_holes_trimmed; if (! needs_drilling) { mesh_view = po.transformed_mesh(); - sla::hollow_mesh(mesh_view, *po.m_hollowing_data->interior, sla::hfRemoveInsideTriangles); + + if (is_hollowed) + sla::hollow_mesh(mesh_view, *po.m_hollowing_data->interior, + sla::hfRemoveInsideTriangles); + BOOST_LOG_TRIVIAL(info) << "Drilling skipped (no holes)."; return; } @@ -200,9 +205,13 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) try { MeshBoolean::cgal::minus(*hollowed_mesh_cgal, *holes_mesh_cgal); hollowed_mesh = MeshBoolean::cgal::cgal_to_triangle_mesh(*hollowed_mesh_cgal); - mesh_view = hollowed_mesh; - sla::remove_inside_triangles(mesh_view, *po.m_hollowing_data->interior); + + if (is_hollowed) + sla::remove_inside_triangles(mesh_view, + *po.m_hollowing_data->interior, + drainholes); + } catch (const std::runtime_error &) { throw Slic3r::SlicingError(L( "Drilling holes into the mesh failed. " From a62262666a2d3ad304bcbe88a3971542e282ff1d Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 3 Mar 2021 09:29:13 +0100 Subject: [PATCH 077/285] Exclude triangles of original interior mesh and drillholes from trimming --- src/libslic3r/SLA/Hollowing.cpp | 12 ++- src/libslic3r/SLA/Hollowing.hpp | 11 ++- src/libslic3r/SLAPrintSteps.cpp | 143 +++++++++++++++++++++++++++++++- src/libslic3r/TriangleMesh.cpp | 18 ++++ src/libslic3r/TriangleMesh.hpp | 6 ++ 5 files changed, 184 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index 6329172669..b387845215 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -421,7 +421,8 @@ void divide_triangle(const DivFace &face, Fn &&visitor) divide_triangle(child2, std::forward(visitor)); } -void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior) +void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior, + const std::vector &exclude_mask) { enum TrPos { posInside, posTouch, posOutside }; @@ -429,6 +430,11 @@ void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior) auto &vertices = mesh.its.vertices; auto bb = mesh.bounding_box(); + bool use_exclude_mask = faces.size() == exclude_mask.size(); + auto is_excluded = [&exclude_mask, use_exclude_mask](size_t face_id) { + return use_exclude_mask && exclude_mask[face_id]; + }; + // TODO: Parallel mode not working yet using exec_policy = ccr_seq; @@ -518,6 +524,10 @@ void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior) exec_policy::for_each(size_t(0), faces.size(), [&] (size_t face_idx) { const Vec3i &face = faces[face_idx]; + // If the triangle is excluded, we need to keep it. + if (is_excluded(face_idx)) + return; + std::array pts = { vertices[face(0)], vertices[face(1)], vertices[face(2)] }; diff --git a/src/libslic3r/SLA/Hollowing.hpp b/src/libslic3r/SLA/Hollowing.hpp index caa7d7b6ba..5d2181e7a6 100644 --- a/src/libslic3r/SLA/Hollowing.hpp +++ b/src/libslic3r/SLA/Hollowing.hpp @@ -81,7 +81,16 @@ void hollow_mesh(TriangleMesh &mesh, const HollowingConfig &cfg, int flags = 0); // Hollowing prepared in "interior", merge with original mesh void hollow_mesh(TriangleMesh &mesh, const Interior &interior, int flags = 0); -void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior); +void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior, + const std::vector &exclude_mask = {}); + +double get_distance(const Vec3f &p, const Interior &interior); + +template +FloatingOnly get_distance(const Vec<3, T> &p, const Interior &interior) +{ + return get_distance(Vec3f(p.template cast()), interior); +} void cut_drainholes(std::vector & obj_slices, const std::vector &slicegrid, diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index fe5d7505f9..455141051b 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -1,3 +1,5 @@ +#include + #include #include #include @@ -142,6 +144,136 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po) } } + +struct FaceHash { + + // A hash is created for each triangle to be identifiable. The hash uses + // only the triangle's geometric traits, not the index in a particular mesh. + std::unordered_set facehash; + + static std::string facekey(const Vec3i &face, + const std::vector &vertices) + { + // Scale to integer to avoid floating points + std::array, 3> pts = { + scaled(vertices[face(0)]), + scaled(vertices[face(1)]), + scaled(vertices[face(2)]) + }; + + // Get the first two sides of the triangle, do a cross product and move + // that vector to the center of the triangle. This encodes all + // information to identify an identical triangle at the same position. + Vec<3, int64_t> a = pts[0] - pts[2], b = pts[1] - pts[2]; + Vec<3, int64_t> c = a.cross(b) + (pts[0] + pts[1] + pts[2]) / 3; + + // Return a concatenated string representation of the coordinates + return std::to_string(c(0)) + std::to_string(c(1)) + std::to_string(c(2)); + }; + + FaceHash(const indexed_triangle_set &its) + { + for (const Vec3i &face : its.indices) { + std::string keystr = facekey(face, its.vertices); + facehash.insert(keystr); + } + } + + bool find(const std::string &key) + { + auto it = facehash.find(key); + return it != facehash.end(); + } +}; + +// Create exclude mask for triangle removal inside hollowed interiors. +// This is necessary when the interior is already part of the mesh which was +// drilled using CGAL mesh boolean operation. Excluded will be the triangles +// originally part of the interior mesh and triangles that make up the drilled +// hole walls. +static std::vector create_exclude_mask( + const indexed_triangle_set &its, + const sla::Interior &interior, + const std::vector &holes) +{ + FaceHash interior_hash{sla::get_mesh(interior).its}; + + std::vector exclude_mask(its.indices.size(), false); + + std::vector< std::vector > neighbor_index = + create_neighbor_index(its); + + auto exclude_neighbors = [&neighbor_index, &exclude_mask](const Vec3i &face) + { + for (int i = 0; i < 3; ++i) { + const std::vector &neighbors = neighbor_index[face(i)]; + for (size_t fi_n : neighbors) exclude_mask[fi_n] = true; + } + }; + + for (size_t fi = 0; fi < its.indices.size(); ++fi) { + auto &face = its.indices[fi]; + + std::string key = + FaceHash::facekey(face, its.vertices); + + if (interior_hash.find(key)) { + exclude_mask[fi] = true; + continue; + } + + if (exclude_mask[fi]) { + exclude_neighbors(face); + continue; + } + + // Lets deal with the holes. All the triangles of a hole and all the + // neighbors of these triangles need to be kept. The neigbors were + // created by CGAL mesh boolean operation that modified the original + // interior inside the input mesh to contain the holes. + Vec3d tr_center = ( + its.vertices[face(0)] + + its.vertices[face(1)] + + its.vertices[face(2)] + ).cast() / 3.; + + // If the center is more than half a mm inside the interior, + // it cannot possibly be part of a hole wall. + if (sla::get_distance(tr_center, interior) < -0.5) + continue; + + Vec3f U = its.vertices[face(1)] - its.vertices[face(0)]; + Vec3f V = its.vertices[face(2)] - its.vertices[face(0)]; + Vec3f C = U.cross(V); + Vec3f face_normal = C.normalized(); + + for (const sla::DrainHole &dh : holes) { + Vec3d dhpos = dh.pos.cast(); + Vec3d dhend = dhpos + dh.normal.cast() * dh.height; + + Linef3 holeaxis{dhpos, dhend}; + + double D_hole_center = line_alg::distance_to(holeaxis, tr_center); + double D_hole = std::abs(D_hole_center - dh.radius); + float dot = dh.normal.dot(face_normal); + + // Empiric tolerances for center distance and normals angle. + // For triangles that are part of a hole wall the angle of + // triangle normal and the hole axis is around 90 degrees, + // so the dot product is around zero. + double D_tol = dh.radius / sla::DrainHole::steps; + float normal_angle_tol = 1.f / sla::DrainHole::steps; + + if (D_hole < D_tol && std::abs(dot) < normal_angle_tol) { + exclude_mask[fi] = true; + exclude_neighbors(face); + } + } + } + + return exclude_mask; +} + // Drill holes into the hollowed/original mesh. void SLAPrint::Steps::drill_holes(SLAPrintObject &po) { @@ -207,10 +339,13 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) hollowed_mesh = MeshBoolean::cgal::cgal_to_triangle_mesh(*hollowed_mesh_cgal); mesh_view = hollowed_mesh; - if (is_hollowed) - sla::remove_inside_triangles(mesh_view, - *po.m_hollowing_data->interior, - drainholes); + if (is_hollowed) { + auto &interior = *po.m_hollowing_data->interior; + std::vector exclude_mask = + create_exclude_mask(mesh_view.its, interior, drainholes); + + sla::remove_inside_triangles(mesh_view, interior, exclude_mask); + } } catch (const std::runtime_error &) { throw Slic3r::SlicingError(L( diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index adb9be64d1..d5a3490877 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -2063,4 +2063,22 @@ TriangleMesh make_sphere(double radius, double fa) return mesh; } +std::vector > create_neighbor_index(const indexed_triangle_set &its) +{ + if (its.vertices.empty()) return {}; + + size_t res = its.indices.size() / its.vertices.size(); + std::vector< std::vector > index(its.vertices.size(), + reserve_vector(res)); + + for (size_t fi = 0; fi < its.indices.size(); ++fi) { + auto &face = its.indices[fi]; + index[face(0)].emplace_back(fi); + index[face(1)].emplace_back(fi); + index[face(2)].emplace_back(fi); + } + + return index; +} + } diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index 9625298f4e..e6f6dc84b8 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -89,6 +89,12 @@ private: std::deque find_unvisited_neighbors(std::vector &facet_visited) const; }; +// Create an index of faces belonging to each vertex. The returned vector can +// be indexed with vertex indices and contains a list of face indices for each +// vertex. +std::vector< std::vector > +create_neighbor_index(const indexed_triangle_set &its); + enum FacetEdgeType { // A general case, the cutting plane intersect a face at two different edges. feGeneral, From 33d6655f26d70f23007afb26345dc302da894f23 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 2 Mar 2021 19:35:46 +0100 Subject: [PATCH 078/285] Clean up hollowing test Needs rethinking anyway --- tests/libslic3r/test_hollowing.cpp | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/tests/libslic3r/test_hollowing.cpp b/tests/libslic3r/test_hollowing.cpp index 2218a27b75..1f5ca38458 100644 --- a/tests/libslic3r/test_hollowing.cpp +++ b/tests/libslic3r/test_hollowing.cpp @@ -2,29 +2,7 @@ #include #include -#include #include "libslic3r/SLA/Hollowing.hpp" -#include -#include "libslic3r/Format/OBJ.hpp" - -#include - -#include - -#if defined(WIN32) || defined(_WIN32) -#define PATH_SEPARATOR R"(\)" -#else -#define PATH_SEPARATOR R"(/)" -#endif - -static Slic3r::TriangleMesh load_model(const std::string &obj_filename) -{ - Slic3r::TriangleMesh mesh; - auto fpath = TEST_DATA_DIR PATH_SEPARATOR + obj_filename; - Slic3r::load_obj(fpath.c_str(), &mesh); - return mesh; -} - TEST_CASE("Hollow two overlapping spheres") { using namespace Slic3r; From 3c2d0b7c6e6668a148e309b2b39bd9d41acdd5d5 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 3 Mar 2021 10:04:26 +0100 Subject: [PATCH 079/285] Tiny cosmetics --- src/libslic3r/SLAPrintSteps.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 455141051b..22fee69761 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -144,7 +144,6 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po) } } - struct FaceHash { // A hash is created for each triangle to be identifiable. The hash uses @@ -214,10 +213,7 @@ static std::vector create_exclude_mask( for (size_t fi = 0; fi < its.indices.size(); ++fi) { auto &face = its.indices[fi]; - std::string key = - FaceHash::facekey(face, its.vertices); - - if (interior_hash.find(key)) { + if (interior_hash.find(FaceHash::facekey(face, its.vertices))) { exclude_mask[fi] = true; continue; } From 158413f4c4b9a127bea21b7ee7128afc579d80ae Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 9 Mar 2021 12:37:17 +0100 Subject: [PATCH 080/285] Disable wxMediaCtrl in wxWidgets build We don't need it. Building on Linux causes problems with gstreamer. fixes #5815, #6160 --- deps/wxWidgets/wxWidgets.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/deps/wxWidgets/wxWidgets.cmake b/deps/wxWidgets/wxWidgets.cmake index c7facc2c62..0c8eaca97e 100644 --- a/deps/wxWidgets/wxWidgets.cmake +++ b/deps/wxWidgets/wxWidgets.cmake @@ -20,6 +20,7 @@ prusaslicer_add_cmake_project(wxWidgets ${_wx_toolkit} "-DCMAKE_DEBUG_POSTFIX:STRING=" -DwxBUILD_DEBUG_LEVEL=0 + -DwxUSE_MEDIACTRL=OFF -DwxUSE_DETECT_SM=OFF -DwxUSE_UNICODE=ON -DwxUSE_OPENGL=ON From b8adfbda662af5a225cbd9fd594e8c3314691d45 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 11 Mar 2021 13:35:19 +0100 Subject: [PATCH 081/285] saving size and position of print host queue dialog, added size column, sorting --- src/slic3r/GUI/PrintHostDialogs.cpp | 104 ++++++++++++++++++++++++++-- src/slic3r/GUI/PrintHostDialogs.hpp | 18 ++++- 2 files changed, 114 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index cb4c222992..af98f32796 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -182,15 +182,24 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent) auto *topsizer = new wxBoxSizer(wxVERTICAL); + std::vector widths; + widths.reserve(6); + if (!load_user_data(UDT_COLS, widths)) { + widths.clear(); + for (size_t i = 0; i < 6; i++) + widths.push_back(-1); + } + job_list = new wxDataViewListCtrl(this, wxID_ANY); // Note: Keep these in sync with Column - job_list->AppendTextColumn(_L("ID"), wxDATAVIEW_CELL_INERT); - job_list->AppendProgressColumn(_L("Progress"), wxDATAVIEW_CELL_INERT); - job_list->AppendTextColumn(_L("Status"), wxDATAVIEW_CELL_INERT); - job_list->AppendTextColumn(_L("Host"), wxDATAVIEW_CELL_INERT); - job_list->AppendTextColumn(_L("Filename"), wxDATAVIEW_CELL_INERT); + job_list->AppendTextColumn(_L("ID"), wxDATAVIEW_CELL_INERT, widths[0], wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE); + job_list->AppendProgressColumn(_L("Progress"), wxDATAVIEW_CELL_INERT, widths[1], wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE); + job_list->AppendTextColumn(_L("Status"), wxDATAVIEW_CELL_INERT, widths[2], wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE); + job_list->AppendTextColumn(_L("Host"), wxDATAVIEW_CELL_INERT, widths[3], wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE); + job_list->AppendTextColumn(_CTX_utf8(L_CONTEXT("Size", "OfFile"), "OfFile"), wxDATAVIEW_CELL_INERT, widths[4], wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE); + job_list->AppendTextColumn(_L("Filename"), wxDATAVIEW_CELL_INERT, widths[5], wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE); job_list->AppendTextColumn(_L("Error Message"), wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER, wxDATAVIEW_COL_HIDDEN); - + auto *btnsizer = new wxBoxSizer(wxHORIZONTAL); btn_cancel = new wxButton(this, wxID_DELETE, _L("Cancel selected")); btn_cancel->Disable(); @@ -207,7 +216,21 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent) topsizer->Add(btnsizer, 0, wxEXPAND); SetSizer(topsizer); - SetSize(wxSize(HEIGHT * em, WIDTH * em)); + std::vector size; + SetSize(load_user_data(UDT_SIZE, size) ? wxSize(size[0] * em, size[1] * em) : wxSize(HEIGHT * em, WIDTH * em)); + + Bind(wxEVT_SIZE, [this, em](wxSizeEvent& evt) { + OnSize(evt); + save_user_data(UDT_SIZE | UDT_POSITION | UDT_COLS); + }); + + std::vector pos; + if (load_user_data(UDT_POSITION, pos)) + SetPosition(wxPoint(pos[0], pos[1])); + + Bind(wxEVT_MOVE, [this, em](wxMoveEvent& evt) { + save_user_data(UDT_SIZE | UDT_POSITION | UDT_COLS); + }); job_list->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxDataViewEvent&) { on_list_select(); }); @@ -238,6 +261,12 @@ void PrintHostQueueDialog::append_job(const PrintHostJob &job) fields.push_back(wxVariant(0)); fields.push_back(wxVariant(_L("Enqueued"))); fields.push_back(wxVariant(job.printhost->get_host())); + boost::system::error_code ec; + boost::uintmax_t size_i = boost::filesystem::file_size(job.upload_data.source_path, ec); + std::string size = ec ? "unknown" : ((size_i >> 10) > 1024 ? std::to_string((float)(size_i >> 10)/1024) + "MB" : std::to_string(size_i >> 10) + "KB"); + if (ec) + BOOST_LOG_TRIVIAL(error) << ec.message(); + fields.push_back(wxVariant(size)); fields.push_back(wxVariant(job.upload_data.upload_path.string())); fields.push_back(wxVariant("")); job_list->AppendItem(fields, static_cast(ST_NEW)); @@ -255,6 +284,8 @@ void PrintHostQueueDialog::on_dpi_changed(const wxRect &suggested_rect) Fit(); Refresh(); + + save_user_data(UDT_SIZE | UDT_POSITION | UDT_COLS); } PrintHostQueueDialog::JobState PrintHostQueueDialog::get_state(int idx) @@ -276,6 +307,8 @@ void PrintHostQueueDialog::set_state(int idx, JobState state) case ST_CANCELLED: job_list->SetValue(_L("Cancelled"), idx, COL_STATUS); break; case ST_COMPLETED: job_list->SetValue(_L("Completed"), idx, COL_STATUS); break; } + // This might be ambigous call, but user data needs to be saved time to time + save_user_data(UDT_SIZE | UDT_POSITION | UDT_COLS); } void PrintHostQueueDialog::on_list_select() @@ -330,6 +363,7 @@ void PrintHostQueueDialog::on_cancel(Event &evt) on_list_select(); } + void PrintHostQueueDialog::get_active_jobs(std::vector>& ret) { int ic = job_list->GetItemCount(); @@ -343,4 +377,60 @@ void PrintHostQueueDialog::get_active_jobs(std::vectordata } +void PrintHostQueueDialog::save_user_data(int udt) +{ + const auto em = GetTextExtent("m").x; + BOOST_LOG_TRIVIAL(error) << "save" << this->GetSize().x / em << " " << this->GetSize().y / em << " " << this->GetPosition().x << " " << this->GetPosition().y; + auto *app_config = wxGetApp().app_config; + if (udt & UserDataType::UDT_SIZE) { + + app_config->set("print_host_queue_dialog_height", std::to_string(this->GetSize().x / em)); + app_config->set("print_host_queue_dialog_width", std::to_string(this->GetSize().y / em)); + } + if (udt & UserDataType::UDT_POSITION) + { + app_config->set("print_host_queue_dialog_x", std::to_string(this->GetPosition().x)); + app_config->set("print_host_queue_dialog_y", std::to_string(this->GetPosition().y)); + } + if (udt & UserDataType::UDT_COLS) + { + for (size_t i = 0; i < job_list->GetColumnCount() - 1; i++) + { + app_config->set("print_host_queue_dialog_column_" + std::to_string(i), std::to_string(job_list->GetColumn(i)->GetWidth())); + } + } +} +bool PrintHostQueueDialog::load_user_data(int udt, std::vector& vector) +{ + auto* app_config = wxGetApp().app_config; + auto hasget = [app_config](const std::string& name, std::vector& vector)->bool { + if (app_config->has(name)) { + vector.push_back(std::stoi(app_config->get(name))); + return true; + } + return false; + }; + if (udt & UserDataType::UDT_SIZE) { + if (!hasget("print_host_queue_dialog_height",vector)) + return false; + if (!hasget("print_host_queue_dialog_width", vector)) + return false; + } + if (udt & UserDataType::UDT_POSITION) + { + if (!hasget("print_host_queue_dialog_x", vector)) + return false; + if (!hasget("print_host_queue_dialog_y", vector)) + return false; + } + if (udt & UserDataType::UDT_COLS) + { + for (size_t i = 0; i < 6; i++) + { + if (!hasget("print_host_queue_dialog_column_" + std::to_string(i), vector)) + return false; + } + } + return true; +} }} diff --git a/src/slic3r/GUI/PrintHostDialogs.hpp b/src/slic3r/GUI/PrintHostDialogs.hpp index 1fda0db667..c185f57be2 100644 --- a/src/slic3r/GUI/PrintHostDialogs.hpp +++ b/src/slic3r/GUI/PrintHostDialogs.hpp @@ -65,6 +65,13 @@ public: void append_job(const PrintHostJob &job); void get_active_jobs(std::vector>& ret); + + virtual bool Show(bool show = true) override + { + if(!show) + save_user_data(UDT_SIZE | UDT_POSITION | UDT_COLS); + return DPIDialog::Show(show); + } protected: void on_dpi_changed(const wxRect &suggested_rect) override; @@ -74,8 +81,9 @@ private: COL_PROGRESS, COL_STATUS, COL_HOST, + COL_SIZE, COL_FILENAME, - COL_ERRORMSG, + COL_ERRORMSG }; enum JobState { @@ -89,6 +97,12 @@ private: enum { HEIGHT = 60, WIDTH = 30, SPACING = 5 }; + enum UserDataType{ + UDT_SIZE = 1, + UDT_POSITION = 2, + UDT_COLS = 4 + }; + wxButton *btn_cancel; wxButton *btn_error; wxDataViewListCtrl *job_list; @@ -105,6 +119,8 @@ private: void on_cancel(Event&); // This vector keep adress and filename of uploads. It is used when checking for running uploads during exit. std::vector> upload_names; + void save_user_data(int); + bool load_user_data(int, std::vector&); }; wxDECLARE_EVENT(EVT_PRINTHOST_PROGRESS, PrintHostQueueDialog::Event); From 58733e68075566062fe5623be5a847ede74d102d Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 11 Mar 2021 16:32:20 +0100 Subject: [PATCH 082/285] print host upload queue dialog - precision in size column --- src/slic3r/GUI/PrintHostDialogs.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index af98f32796..605e143492 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -263,10 +263,14 @@ void PrintHostQueueDialog::append_job(const PrintHostJob &job) fields.push_back(wxVariant(job.printhost->get_host())); boost::system::error_code ec; boost::uintmax_t size_i = boost::filesystem::file_size(job.upload_data.source_path, ec); - std::string size = ec ? "unknown" : ((size_i >> 10) > 1024 ? std::to_string((float)(size_i >> 10)/1024) + "MB" : std::to_string(size_i >> 10) + "KB"); - if (ec) + std::stringstream stream; + if (ec) { + stream << "unknown"; + size_i = 0; BOOST_LOG_TRIVIAL(error) << ec.message(); - fields.push_back(wxVariant(size)); + } else + stream << std::fixed << std::setprecision(2) << ((float)size_i / 1024 / 1024) << "MB"; + fields.push_back(wxVariant(stream.str())); fields.push_back(wxVariant(job.upload_data.upload_path.string())); fields.push_back(wxVariant("")); job_list->AppendItem(fields, static_cast(ST_NEW)); From 08a826d2372be9c9c8b0ca4f473f3b27c2dee4e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Fri, 12 Mar 2021 10:30:06 +0100 Subject: [PATCH 083/285] Added a missing includes --- src/slic3r/GUI/PrintHostDialogs.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index 605e143492..a094b70e9a 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -1,6 +1,7 @@ #include "PrintHostDialogs.hpp" #include +#include #include #include @@ -13,6 +14,9 @@ #include #include +#include +#include + #include "GUI.hpp" #include "GUI_App.hpp" #include "MsgDialog.hpp" From c41df487bb5821a9271e547ab97d424e1e9b6afa Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 8 Feb 2021 16:14:35 +0100 Subject: [PATCH 084/285] Notifications management and rendering refactoring. With warning notification Model out of bed reworked to not show after dismiss. --- src/slic3r/GUI/3DScene.cpp | 51 ++++ src/slic3r/GUI/3DScene.hpp | 1 + src/slic3r/GUI/GLCanvas3D.cpp | 65 +++-- src/slic3r/GUI/GLCanvas3D.hpp | 3 +- src/slic3r/GUI/NotificationManager.cpp | 385 ++++++++++--------------- src/slic3r/GUI/NotificationManager.hpp | 112 ++++--- 6 files changed, 293 insertions(+), 324 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 4cab8f3db9..6c226cd74d 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -845,6 +845,57 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M return contained_min_one; } +bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, bool& partlyOut, bool& fullyOut) +{ + if (config == nullptr) + return false; + + const ConfigOptionPoints* opt = dynamic_cast(config->option("bed_shape")); + if (opt == nullptr) + return false; + + BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values)); + BoundingBoxf3 print_volume(Vec3d(unscale(bed_box_2D.min(0)), unscale(bed_box_2D.min(1)), 0.0), Vec3d(unscale(bed_box_2D.max(0)), unscale(bed_box_2D.max(1)), config->opt_float("max_print_height"))); + // Allow the objects to protrude below the print bed + print_volume.min(2) = -1e10; + print_volume.min(0) -= BedEpsilon; + print_volume.min(1) -= BedEpsilon; + print_volume.max(0) += BedEpsilon; + print_volume.max(1) += BedEpsilon; + + bool contained_min_one = false; + + partlyOut = false; + fullyOut = false; + for (GLVolume* volume : this->volumes) + { + if ((volume == nullptr) || volume->is_modifier || (volume->is_wipe_tower && !volume->shader_outside_printer_detection_enabled) || ((volume->composite_id.volume_id < 0) && !volume->shader_outside_printer_detection_enabled)) + continue; + + const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box(); + bool contained = print_volume.contains(bb); + + volume->is_outside = !contained; + if (!volume->printable) + continue; + + if (contained) + contained_min_one = true; + + if (volume->is_outside) { + if (print_volume.intersects(bb)) + partlyOut = true; + else + fullyOut = true; + } + } + /* + if (out_state != nullptr) + *out_state = state; + */ + return contained_min_one; +} + void GLVolumeCollection::reset_outside_state() { for (GLVolume* volume : this->volumes) diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index e4d9c60672..2ae2a36b29 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -569,6 +569,7 @@ public: // returns true if all the volumes are completely contained in the print volume // returns the containment state in the given out_state, if non-null bool check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state); + bool check_outside_state(const DynamicPrintConfig* config, bool& partlyOut, bool& fullyOut); void reset_outside_state(); void update_colors_by_extruder(const DynamicPrintConfig* config); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index a45f61cabd..0a2b5cd655 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -641,6 +641,7 @@ void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool error = true; break; } + BOOST_LOG_TRIVIAL(error) << state << " : " << text ; auto ¬ification_manager = *wxGetApp().plater()->get_notification_manager(); if (state) { if(error) @@ -1620,9 +1621,6 @@ void GLCanvas3D::render() wxGetApp().plater()->init_environment_texture(); #endif // ENABLE_ENVIRONMENT_MAP - m_render_timer.Stop(); - m_extra_frame_requested_delayed = std::numeric_limits::max(); - const Size& cnv_size = get_canvas_size(); // Probably due to different order of events on Linux/GTK2, when one switched from 3D scene // to preview, this was called before canvas had its final size. It reported zero width @@ -1754,7 +1752,7 @@ void GLCanvas3D::render() m_tooltip.render(m_mouse.position, *this); wxGetApp().plater()->get_mouse3d_controller().render_settings_dialog(*this); - wxGetApp().plater()->get_notification_manager()->render_notifications(get_overlay_window_width()); + wxGetApp().plater()->get_notification_manager()->render_notifications(*this, get_overlay_window_width()); wxGetApp().imgui()->render(); @@ -2238,24 +2236,24 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re // checks for geometry outside the print volume to render it accordingly if (!m_volumes.empty()) { - ModelInstanceEPrintVolumeState state; - - const bool contained_min_one = m_volumes.check_outside_state(m_config, &state); + bool partlyOut = false; + bool fullyOut = false; + const bool contained_min_one = m_volumes.check_outside_state(m_config, partlyOut, fullyOut); #if ENABLE_WARNING_TEXTURE_REMOVAL - _set_warning_notification(EWarning::ObjectClashed, state == ModelInstancePVS_Partly_Outside); - _set_warning_notification(EWarning::ObjectOutside, state == ModelInstancePVS_Fully_Outside); - if (printer_technology != ptSLA || state == ModelInstancePVS_Inside) + _set_warning_notification(EWarning::ObjectClashed, partlyOut); + _set_warning_notification(EWarning::ObjectOutside, fullyOut); + if (printer_technology != ptSLA || !contained_min_one) _set_warning_notification(EWarning::SlaSupportsOutside, false); #else - _set_warning_texture(WarningTexture::ObjectClashed, state == ModelInstancePVS_Partly_Outside); - _set_warning_texture(WarningTexture::ObjectOutside, state == ModelInstancePVS_Fully_Outside); - if(printer_technology != ptSLA || state == ModelInstancePVS_Inside) + _set_warning_texture(WarningTexture::ObjectClashed, partlyOut); + _set_warning_texture(WarningTexture::ObjectOutside, fullyOut); + if(printer_technology != ptSLA || !contained_min_one) _set_warning_texture(WarningTexture::SlaSupportsOutside, false); #endif // ENABLE_WARNING_TEXTURE_REMOVAL post_event(Event(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, - contained_min_one && !m_model->objects.empty() && state != ModelInstancePVS_Partly_Outside)); + contained_min_one && !m_model->objects.empty() && !partlyOut)); } else { #if ENABLE_WARNING_TEXTURE_REMOVAL @@ -2442,13 +2440,13 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) if (!m_initialized) return; - // FIXME m_dirty |= m_main_toolbar.update_items_state(); m_dirty |= m_undoredo_toolbar.update_items_state(); m_dirty |= wxGetApp().plater()->get_view_toolbar().update_items_state(); m_dirty |= wxGetApp().plater()->get_collapse_toolbar().update_items_state(); bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(wxGetApp().plater()->get_camera()); m_dirty |= mouse3d_controller_applied; + m_dirty |= wxGetApp().plater()->get_notification_manager()->update_notifications(*this); if (!m_dirty) return; @@ -2982,30 +2980,39 @@ void GLCanvas3D::on_timer(wxTimerEvent& evt) void GLCanvas3D::on_render_timer(wxTimerEvent& evt) { - // If slicer is not top window -> restart timer with one second to try again - wxWindow* p = dynamic_cast(wxGetApp().plater()); - while (p->GetParent() != nullptr) - p = p->GetParent(); - wxTopLevelWindow* top_level_wnd = dynamic_cast(p); - if (!top_level_wnd->IsActive()) { - request_extra_frame_delayed(1000); - return; - } - //render(); - m_dirty = true; - wxWakeUpIdle(); + // no need to do anything here + // right after this event is recieved, idle event is fired + + //m_dirty = true; + //wxWakeUpIdle(); } -void GLCanvas3D::request_extra_frame_delayed(int miliseconds) + +void GLCanvas3D::schedule_extra_frame(int miliseconds) { + // Schedule idle event right now + if (miliseconds == 0) + { + // We want to wakeup idle evnt but most likely this is call inside render cycle so we need to wait + if (m_in_render) + miliseconds = 33; + else { + m_dirty = true; + wxWakeUpIdle(); + return; + } + } + // Start timer int64_t now = timestamp_now(); + // Timer is not running if (! m_render_timer.IsRunning()) { m_extra_frame_requested_delayed = miliseconds; m_render_timer.StartOnce(miliseconds); m_render_timer_start = now; + // Timer is running - restart only if new period is shorter than remaning period } else { const int64_t remaining_time = (m_render_timer_start + m_extra_frame_requested_delayed) - now; - if (miliseconds < remaining_time) { + if (miliseconds + 20 < remaining_time) { m_render_timer.Stop(); m_extra_frame_requested_delayed = miliseconds; m_render_timer.StartOnce(miliseconds); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 10294931fe..f4d862b66c 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -743,7 +743,8 @@ public: void msw_rescale(); void request_extra_frame() { m_extra_frame_requested = true; } - void request_extra_frame_delayed(int miliseconds); + + void schedule_extra_frame(int miliseconds); int get_main_toolbar_item_id(const std::string& name) const { return m_main_toolbar.get_item_id(name); } void force_main_toolbar_left_action(int item_id) { m_main_toolbar.force_left_action(item_id, *this); } diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index a6fd9cfd3c..41c8e8f6fc 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -22,9 +22,6 @@ static constexpr float SPACE_RIGHT_PANEL = 10.0f; static constexpr float FADING_OUT_DURATION = 2.0f; // Time in Miliseconds after next render when fading out is requested static constexpr int FADING_OUT_TIMEOUT = 100; -// If timeout is changed to higher than 1 second, substract_time call should be revorked -//static constexpr int MAX_TIMEOUT_MILISECONDS = 1000; -//static constexpr int MAX_TIMEOUT_SECONDS = 1; namespace Slic3r { namespace GUI { @@ -131,35 +128,29 @@ void NotificationManager::NotificationIDProvider::release_id(int) {} NotificationManager::PopNotification::PopNotification(const NotificationData &n, NotificationIDProvider &id_provider, wxEvtHandler* evt_handler) : m_data (n) , m_id_provider (id_provider) - , m_remaining_time (n.duration) - , m_last_remaining_time (n.duration) - , m_counting_down (n.duration != 0) , m_text1 (n.text1) , m_hypertext (n.hypertext) , m_text2 (n.text2) , m_evt_handler (evt_handler) , m_notification_start (GLCanvas3D::timestamp_now()) -{ - //init(); -} +{} void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width) { - if (!m_initialized) + + if (m_state == EState::Unknown) init(); - if (m_hidden) { + if (m_state == EState::Hidden) { m_top_y = initial_y - GAP_WIDTH; return; } - if (m_fading_out) - m_last_render_fading = GLCanvas3D::timestamp_now(); - - Size cnv_size = canvas.get_canvas_size(); + Size cnv_size = canvas.get_canvas_size(); ImGuiWrapper& imgui = *wxGetApp().imgui(); - ImVec2 mouse_pos = ImGui::GetMousePos(); - float right_gap = SPACE_RIGHT_PANEL + (move_from_overlay ? overlay_width + m_line_height * 5 : 0); + ImVec2 mouse_pos = ImGui::GetMousePos(); + float right_gap = SPACE_RIGHT_PANEL + (move_from_overlay ? overlay_width + m_line_height * 5 : 0); + bool fading_pop = false; if (m_line_height != ImGui::CalcTextSize("A").y) init(); @@ -174,54 +165,46 @@ void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float init imgui.set_next_window_size(m_window_width, m_window_height, ImGuiCond_Always); // find if hovered - m_hovered = false; + if (m_state == EState::Hovered) + m_state = EState::Shown; + if (mouse_pos.x < win_pos.x && mouse_pos.x > win_pos.x - m_window_width && mouse_pos.y > win_pos.y && mouse_pos.y < win_pos.y + m_window_height) { ImGui::SetNextWindowFocus(); - m_hovered = true; + m_state = EState::Hovered; } - + // color change based on fading out - bool fading_pop = false; - if (m_fading_out) { - Notifications_Internal::push_style_color(ImGuiCol_WindowBg, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), m_fading_out, m_current_fade_opacity); - Notifications_Internal::push_style_color(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text), m_fading_out, m_current_fade_opacity); + if (m_state == EState::FadingOut) { + Notifications_Internal::push_style_color(ImGuiCol_WindowBg, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), true, m_current_fade_opacity); + Notifications_Internal::push_style_color(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text), true, m_current_fade_opacity); fading_pop = true; } - + // background color if (m_is_gray) { ImVec4 backcolor(0.7f, 0.7f, 0.7f, 0.5f); - Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_fading_out, m_current_fade_opacity); + Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity); } else if (m_data.level == NotificationLevel::ErrorNotification) { ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg); backcolor.x += 0.3f; - Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_fading_out, m_current_fade_opacity); + Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity); } else if (m_data.level == NotificationLevel::WarningNotification) { ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg); backcolor.x += 0.3f; backcolor.y += 0.15f; - Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_fading_out, m_current_fade_opacity); + Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity); } - - // name of window - probably indentifies window and is shown so last_end add whitespaces according to id + + // name of window indentifies window - has to be unique string if (m_id == 0) m_id = m_id_provider.allocate_id(); std::string name = "!!Ntfctn" + std::to_string(m_id); + if (imgui.begin(name, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar)) { ImVec2 win_size = ImGui::GetWindowSize(); - //FIXME: dont forget to us this for texts - //GUI::format(_utf8(L())); - - /* - //countdown numbers - ImGui::SetCursorPosX(15); - ImGui::SetCursorPosY(15); - imgui.text(std::to_string(m_remaining_time).c_str()); - */ - render_left_sign(imgui); render_text(imgui, win_size.x, win_size.y, win_pos.x, win_pos.y); render_close_button(imgui, win_size.x, win_size.y, win_pos.x, win_pos.y); @@ -253,6 +236,7 @@ void NotificationManager::PopNotification::count_spaces() m_window_width_offset = m_left_indentation + m_line_height * 3.f; m_window_width = m_line_height * 25; } + void NotificationManager::PopNotification::init() { std::string text = m_text1 + " " + m_hypertext; @@ -306,7 +290,7 @@ void NotificationManager::PopNotification::init() } if (m_lines_count == 3) m_multiline = true; - m_initialized = true; + m_state = EState::Shown; } void NotificationManager::PopNotification::set_next_window_size(ImGuiWrapper& imgui) { @@ -423,8 +407,8 @@ void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui, m_multiline = true; set_next_window_size(imgui); } - else { - m_close_pending = on_text_click(); + else if (on_text_click()) { + close(); } } ImGui::PopStyleColor(); @@ -432,12 +416,12 @@ void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui, ImGui::PopStyleColor(); //hover color - ImVec4 orange_color = ImVec4(.99f, .313f, .0f, 1.0f);//ImGui::GetStyleColorVec4(ImGuiCol_Button); + ImVec4 orange_color = ImVec4(.99f, .313f, .0f, 1.0f); if (ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly)) orange_color.y += 0.2f; //text - Notifications_Internal::push_style_color(ImGuiCol_Text, orange_color, m_fading_out, m_current_fade_opacity); + Notifications_Internal::push_style_color(ImGuiCol_Text, orange_color, m_state == EState::FadingOut, m_current_fade_opacity); ImGui::SetCursorPosX(text_x); ImGui::SetCursorPosY(text_y); imgui.text(text.c_str()); @@ -448,7 +432,7 @@ void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui, lineEnd.y -= 2; ImVec2 lineStart = lineEnd; lineStart.x = ImGui::GetItemRectMin().x; - ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, IM_COL32((int)(orange_color.x * 255), (int)(orange_color.y * 255), (int)(orange_color.z * 255), (int)(orange_color.w * 255.f * (m_fading_out ? m_current_fade_opacity : 1.f)))); + ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, IM_COL32((int)(orange_color.x * 255), (int)(orange_color.y * 255), (int)(orange_color.z * 255), (int)(orange_color.w * 255.f * (m_state == EState::FadingOut ? m_current_fade_opacity : 1.f)))); } @@ -458,12 +442,11 @@ void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& img ImVec2 win_pos(win_pos_x, win_pos_y); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f)); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f)); - Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_fading_out, m_current_fade_opacity); - Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_fading_out, m_current_fade_opacity); + Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity); + Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity); ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f)); - //button - if part if treggered std::string button_text; button_text = ImGui::CloseNotifButton; @@ -479,7 +462,7 @@ void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& img ImGui::SetCursorPosY(win_size.y / 2 - button_size.y); if (imgui.button(button_text.c_str(), button_size.x, button_size.y)) { - m_close_pending = true; + close(); } //invisible large button @@ -487,7 +470,7 @@ void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& img ImGui::SetCursorPosY(0); if (imgui.button(" ", m_line_height * 2.125, win_size.y - ( m_minimize_b_visible ? 2 * m_line_height : 0))) { - m_close_pending = true; + close(); } ImGui::PopStyleColor(); ImGui::PopStyleColor(); @@ -510,9 +493,9 @@ void NotificationManager::PopNotification::render_minimize_button(ImGuiWrapper& { ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f)); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f)); - Notifications_Internal::push_style_color(ImGuiCol_ButtonActive, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), m_fading_out, m_current_fade_opacity); - Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_fading_out, m_current_fade_opacity); - Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_fading_out, m_current_fade_opacity); + Notifications_Internal::push_style_color(ImGuiCol_ButtonActive, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), m_state == EState::FadingOut, m_current_fade_opacity); + Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity); + Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity); //button - if part if treggered @@ -564,71 +547,56 @@ bool NotificationManager::PopNotification::compare_text(const std::string& text) return false; } -void NotificationManager::PopNotification::update_state() +void NotificationManager::PopNotification::update_state(bool paused, const int64_t delta) { - if (!m_initialized) + if (m_state == EState::Unknown) init(); m_next_render = std::numeric_limits::max(); - if (m_hidden) { - m_state = EState::Hidden; + if (m_state == EState::Hidden) { return; } int64_t now = GLCanvas3D::timestamp_now(); - if (m_hovered) { - // reset fading - m_fading_out = false; + // reset timers - hovered state is set in render + if (m_state == EState::Hovered) { m_current_fade_opacity = 1.0f; - m_remaining_time = m_data.duration; m_notification_start = now; - } - - - - if (m_counting_down) { + // Timers when not fading + } else if (m_data.duration != 0 && !paused) { int64_t up_time = now - m_notification_start; - - if (m_fading_out && m_current_fade_opacity <= 0.0f) - m_finished = true; - else if (!m_fading_out && /*m_remaining_time <=0*/up_time >= m_data.duration * 1000) { - m_fading_out = true; - m_fading_start = now; - m_last_render_fading = now; - } else if (!m_fading_out) { - m_next_render = m_data.duration * 1000 - up_time;//std::min(/*m_data.duration * 1000 - up_time*/m_remaining_time * 1000, MAX_TIMEOUT_MILISECONDS); - } - + if (m_state != EState::FadingOut && up_time >= m_data.duration * 1000) { + m_state = EState::FadingOut; + m_fading_start = now; + } else if (m_state != EState::FadingOut) { + m_next_render = m_data.duration * 1000 - up_time; + } } - - if (m_finished) { + // Timers when fading + if (m_state == EState::FadingOut && !paused) { + int64_t curr_time = now - m_fading_start; + int64_t next_render = FADING_OUT_TIMEOUT - delta; + m_current_fade_opacity = std::clamp(1.0f - 0.001f * static_cast(curr_time) / FADING_OUT_DURATION, 0.0f, 1.0f); + if (m_current_fade_opacity <= 0.0f) + m_state = EState::Finished; + else if (next_render < 0) + m_next_render = 0; + else + m_next_render = next_render; + } + + if (m_state == EState::Finished) { + m_next_render = 0; + return; + } + + if (m_state == EState::ClosePending) { m_state = EState::Finished; m_next_render = 0; return; } - if (m_close_pending) { - m_finished = true; - m_state = EState::ClosePending; - m_next_render = 0; - return; - } - if (m_fading_out) { - if (!m_paused) { - m_state = EState::FadingOutStatic; - int64_t curr_time = now - m_fading_start; - int64_t no_render_time = now - m_last_render_fading; - m_current_fade_opacity = std::clamp(1.0f - 0.001f * static_cast(curr_time) / FADING_OUT_DURATION, 0.0f, 1.0f); - auto next_render = FADING_OUT_TIMEOUT - no_render_time; - if (next_render <= 0) { - //m_last_render_fading = GLCanvas3D::timestamp_now(); - m_state = EState::FadingOutRender; - m_next_render = 0; - } else - m_next_render = next_render; - } - } } NotificationManager::SlicingCompleteLargeNotification::SlicingCompleteLargeNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, bool large) : @@ -672,9 +640,10 @@ void NotificationManager::SlicingCompleteLargeNotification::set_print_info(const void NotificationManager::SlicingCompleteLargeNotification::set_large(bool l) { m_is_large = l; - m_counting_down = !l; + //FIXME this information should not be lost (change m_data?) +// m_counting_down = !l; m_hypertext = l ? _u8L("Export G-Code.") : std::string(); - m_hidden = !l; + m_state = l ? EState::Shown : EState::Hidden; } //---------------ExportFinishedNotification----------- void NotificationManager::ExportFinishedNotification::count_spaces() @@ -733,8 +702,8 @@ void NotificationManager::ExportFinishedNotification::render_eject_button(ImGuiW ImVec2 win_pos(win_pos_x, win_pos_y); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f)); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f)); - Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_fading_out, m_current_fade_opacity); - Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_fading_out, m_current_fade_opacity); + Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity); + Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity); ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f)); std::string button_text; @@ -768,7 +737,7 @@ void NotificationManager::ExportFinishedNotification::render_eject_button(ImGuiW assert(m_evt_handler != nullptr); if (m_evt_handler != nullptr) wxPostEvent(m_evt_handler, EjectDriveNotificationClickedEvent(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED)); - m_close_pending = true; + close(); } //invisible large button @@ -779,7 +748,7 @@ void NotificationManager::ExportFinishedNotification::render_eject_button(ImGuiW assert(m_evt_handler != nullptr); if (m_evt_handler != nullptr) wxPostEvent(m_evt_handler, EjectDriveNotificationClickedEvent(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED)); - m_close_pending = true; + close(); } ImGui::PopStyleColor(); ImGui::PopStyleColor(); @@ -807,22 +776,12 @@ void NotificationManager::ProgressBarNotification::render_text(ImGuiWrapper& img void NotificationManager::ProgressBarNotification::render_bar(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) { ImVec4 orange_color = ImVec4(.99f, .313f, .0f, 1.0f); - float invisible_length = 0;//((float)(m_data.duration - m_remaining_time) / (float)m_data.duration * win_size_x); - //invisible_length -= win_size_x / ((float)m_data.duration * 60.f) * (60 - m_countdown_frame); + float invisible_length = 0; + ImVec2 lineEnd = ImVec2(win_pos_x - invisible_length - m_window_width_offset, win_pos_y + win_size_y/2 + m_line_height / 2); ImVec2 lineStart = ImVec2(win_pos_x - win_size_x + m_left_indentation, win_pos_y + win_size_y/2 + m_line_height / 2); ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, IM_COL32((int)(orange_color.x * 255), (int)(orange_color.y * 255), (int)(orange_color.z * 255), (1.0f * 255.f)), m_line_height * 0.7f); - /* - //countdown line - ImVec4 orange_color = ImGui::GetStyleColorVec4(ImGuiCol_Button); - float invisible_length = ((float)(m_data.duration - m_remaining_time) / (float)m_data.duration * win_size_x); - invisible_length -= win_size_x / ((float)m_data.duration * 60.f) * (60 - m_countdown_frame); - ImVec2 lineEnd = ImVec2(win_pos_x - invisible_length, win_pos_y + win_size_y - 5); - ImVec2 lineStart = ImVec2(win_pos_x - win_size_x, win_pos_y + win_size_y - 5); - ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, IM_COL32((int)(orange_color.x * 255), (int)(orange_color.y * 255), (int)(orange_color.z * 255), (int)(orange_color.picture_width * 255.f * (m_fading_out ? m_current_fade_opacity : 1.f))), 2.f); - if (!m_paused) - m_countdown_frame++; - */ + } //------NotificationManager-------- NotificationManager::NotificationManager(wxEvtHandler* evt_handler) : @@ -881,12 +840,7 @@ void NotificationManager::push_plater_error_notification(const std::string& text { push_notification_data({ NotificationType::PlaterError, NotificationLevel::ErrorNotification, 0, _u8L("ERROR:") + "\n" + text }, 0); } -void NotificationManager::push_plater_warning_notification(const std::string& text) -{ - push_notification_data({ NotificationType::PlaterWarning, NotificationLevel::WarningNotification, 0, _u8L("WARNING:") + "\n" + text }, 0); - // dissaper if in preview - set_in_preview(m_in_preview); -} + void NotificationManager::close_plater_error_notification(const std::string& text) { for (std::unique_ptr ¬ification : m_pop_notifications) { @@ -895,11 +849,32 @@ void NotificationManager::close_plater_error_notification(const std::string& tex } } } + +void NotificationManager::push_plater_warning_notification(const std::string& text) +{ + // Find if was not hidden + for (std::unique_ptr& notification : m_pop_notifications) { + if (notification->get_type() == NotificationType::PlaterWarning && notification->compare_text(_u8L("WARNING:") + "\n" + text)) { + if (notification->get_state() == PopNotification::EState::Hidden) { + //dynamic_cast(notification.get())->show(); + return; + } + } + } + + NotificationData data{ NotificationType::PlaterWarning, NotificationLevel::WarningNotification, 0, _u8L("WARNING:") + "\n" + text }; + + auto notification = std::make_unique(data, m_id_provider, m_evt_handler); + push_notification_data(std::move(notification), 0); + // dissaper if in preview + set_in_preview(m_in_preview); +} + void NotificationManager::close_plater_warning_notification(const std::string& text) { for (std::unique_ptr ¬ification : m_pop_notifications) { if (notification->get_type() == NotificationType::PlaterWarning && notification->compare_text(_u8L("WARNING:") + "\n" + text)) { - notification->close(); + dynamic_cast(notification.get())->real_close(); } } } @@ -1008,7 +983,8 @@ void NotificationManager::set_progress_bar_percentage(const std::string& text, f for (std::unique_ptr& notification : m_pop_notifications) { if (notification->get_type() == NotificationType::ProgressBar && notification->compare_text(text)) { dynamic_cast(notification.get())->set_percentage(percentage); - wxGetApp().plater()->get_current_canvas3D()->request_extra_frame(); + // FIX ME: this is massive gpu eater (render every frame) + wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); found = true; } } @@ -1035,20 +1011,19 @@ bool NotificationManager::push_notification_data(std::unique_ptractivate_existing(notification.get())) { m_pop_notifications.back()->update(notification->get_data()); - canvas.request_extra_frame_delayed(33); + canvas.schedule_extra_frame(0); return false; } else { m_pop_notifications.emplace_back(std::move(notification)); - canvas.request_extra_frame_delayed(33); + canvas.schedule_extra_frame(0); return true; } } -void NotificationManager::render_notifications(float overlay_width) +void NotificationManager::render_notifications(GLCanvas3D& canvas, float overlay_width) { sort_notifications(); - - GLCanvas3D& canvas = *wxGetApp().plater()->get_current_canvas3D(); + float last_y = 0.0f; for (const auto& notification : m_pop_notifications) { @@ -1059,9 +1034,54 @@ void NotificationManager::render_notifications(float overlay_width) } } - update_notifications(); + m_last_render = GLCanvas3D::timestamp_now(); } +bool NotificationManager::update_notifications(GLCanvas3D& canvas) +{ + // no update if not top window + wxWindow* p = dynamic_cast(wxGetApp().plater()); + while (p->GetParent() != nullptr) + p = p->GetParent(); + wxTopLevelWindow* top_level_wnd = dynamic_cast(p); + if (!top_level_wnd->IsActive()) + return false; + + // next_render() returns numeric_limits::max if no need for frame + const int64_t max = std::numeric_limits::max(); + int64_t next_render = max; + const int64_t time_since_render = GLCanvas3D::timestamp_now() - m_last_render; + // During render, each notification detects if its currently hovered and changes its state to EState::Hovered + // If any notification is hovered, all restarts its countdown + bool hover = false; + for (const std::unique_ptr& notification : m_pop_notifications) { + if (notification->is_hovered()) { + hover = true; + break; + } + } + // update state of all notif and erase finished + for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end();) { + std::unique_ptr& notification = *it; + notification->update_state(hover, time_since_render); + next_render = std::min(next_render, notification->next_render()); + if (notification->get_state() == PopNotification::EState::Finished) + it = m_pop_notifications.erase(it); + else + ++it; + } + + // render needed right now + if (next_render < 20) + return true; + // request next frame + if (next_render < max) + canvas.schedule_extra_frame(int(next_render)); + + return false; +} +>>>>>>> 6df0d8ff81... Notifications management and rendering refactoring. + void NotificationManager::sort_notifications() { // Stable sorting, so that the order of equal ranges is stable. @@ -1112,103 +1132,6 @@ void NotificationManager::set_in_preview(bool preview) } } -void NotificationManager::update_notifications() -{ - // no update if not top window - wxWindow* p = dynamic_cast(wxGetApp().plater()); - while (p->GetParent() != nullptr) - p = p->GetParent(); - wxTopLevelWindow* top_level_wnd = dynamic_cast(p); - if (!top_level_wnd->IsActive()) - return; - - //static size_t last_size = m_pop_notifications.size(); - - //request frames - int64_t next_render = std::numeric_limits::max(); - for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end();) { - std::unique_ptr& notification = *it; - notification->set_paused(m_hovered); - notification->update_state(); - next_render = std::min(next_render, notification->next_render()); - if (notification->get_state() == PopNotification::EState::Finished) - it = m_pop_notifications.erase(it); - else { - - ++it; - } - } - /* - m_requires_update = false; - for (const std::unique_ptr& notification : m_pop_notifications) { - if (notification->requires_update()) { - m_requires_update = true; - break; - } - } - */ - // update hovering state - m_hovered = false; - for (const std::unique_ptr& notification : m_pop_notifications) { - if (notification->is_hovered()) { - m_hovered = true; - break; - } - } - - /* - // Reuire render if some notification was just deleted. - size_t curr_size = m_pop_notifications.size(); - m_requires_render = m_hovered || (last_size != curr_size); - last_size = curr_size; - - // Ask notification if it needs render - if (!m_requires_render) { - for (const std::unique_ptr& notification : m_pop_notifications) { - if (notification->requires_render()) { - m_requires_render = true; - break; - } - } - } - // Make sure there will be update after last notification erased - if (m_requires_render) - m_requires_update = true; - */ - - - if (next_render == 0) - wxGetApp().plater()->get_current_canvas3D()->request_extra_frame_delayed(33); //few milliseconds to get from GLCanvas::render - else if (next_render < std::numeric_limits::max()) - wxGetApp().plater()->get_current_canvas3D()->request_extra_frame_delayed(int(next_render)); - - /* - // actualizate timers - wxWindow* p = dynamic_cast(wxGetApp().plater()); - while (p->GetParent() != nullptr) - p = p->GetParent(); - wxTopLevelWindow* top_level_wnd = dynamic_cast(p); - if (!top_level_wnd->IsActive()) - return; - - { - // Control the fade-out. - // time in seconds - long now = wxGetLocalTime(); - // Pausing fade-out when the mouse is over some notification. - if (!m_hovered && m_last_time < now) { - if (now - m_last_time >= MAX_TIMEOUT_SECONDS) { - for (auto& notification : m_pop_notifications) { - //if (notification->get_state() != PopNotification::EState::Static) - notification->substract_remaining_time(MAX_TIMEOUT_SECONDS); - } - m_last_time = now; - } - } - } - */ -} - bool NotificationManager::has_slicing_error_notification() { return std::any_of(m_pop_notifications.begin(), m_pop_notifications.end(), [](auto &n) { diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 62c4ea845e..4c584d3663 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -147,14 +147,15 @@ public: // finds ExportFinished notification and closes it if it was to removable device void device_ejected(); // renders notifications in queue and deletes expired ones - void render_notifications(float overlay_width); + void render_notifications(GLCanvas3D& canvas, float overlay_width); // finds and closes all notifications of given type void close_notification_of_type(const NotificationType type); // Which view is active? Plater or G-code preview? Hide warnings in G-code preview. void set_in_preview(bool preview); // Move to left to avoid colision with variable layer height gizmo. void set_move_from_overlay(bool move) { m_move_from_overlay = move; } - + // perform update_state on each notification and ask for more frames if needed, return true for render needed + bool update_notifications(GLCanvas3D& canvas); private: // duration 0 means not disapearing struct NotificationData { @@ -192,23 +193,24 @@ private: enum class EState { - Unknown, + Unknown, // NOT initialized Hidden, - FadingOutRender, // Requesting Render - FadingOutStatic, + Shown, // Requesting Render at some time if duration != 0 + FadingOut, // Requesting Render at some time ClosePending, // Requesting Render Finished, // Requesting Render + Hovered, // Followed by Shown + Paused }; PopNotification(const NotificationData &n, NotificationIDProvider &id_provider, wxEvtHandler* evt_handler); virtual ~PopNotification() { if (m_id) m_id_provider.release_id(m_id); } void render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width); // close will dissapear notification on next render - void close() { m_close_pending = true; } + virtual void close() { m_state = EState::ClosePending; } // data from newer notification of same type void update(const NotificationData& n); - bool is_finished() const { return m_finished || m_close_pending; } - bool is_hovered() const { return m_hovered; } + bool is_finished() const { return m_state == EState::ClosePending || m_state == EState::Finished; } // returns top after movement float get_top() const { return m_top_y; } //returns top in actual frame @@ -216,21 +218,15 @@ private: const NotificationType get_type() const { return m_data.type; } const NotificationData get_data() const { return m_data; } const bool is_gray() const { return m_is_gray; } - // Call equals one second down - void substract_remaining_time(int seconds) { m_remaining_time -= seconds; } void set_gray(bool g) { m_is_gray = g; } - void set_paused(bool p) { m_paused = p; } bool compare_text(const std::string& text); - void hide(bool h) { m_hidden = h; } - // sets m_next_render with time of next mandatory rendering - void update_state(); - int64_t next_render() const { return m_next_render; } - /* - bool requires_render() const { return m_state == EState::FadingOutRender || m_state == EState::ClosePending || m_state == EState::Finished; } - bool requires_update() const { return m_state != EState::Hidden; } - */ - EState get_state() const { return m_state; } - protected: + void hide(bool h) { m_state = h ? EState::Hidden : EState::Unknown; } + // sets m_next_render with time of next mandatory rendering. Delta is time since last render. + void update_state(bool paused, const int64_t delta); + int64_t next_render() const { return is_finished() ? 0 : m_next_render; } + EState get_state() const { return m_state; } + bool is_hovered() const { return m_state == EState::Hovered; } + // Call after every size change void init(); // Part of init() @@ -254,45 +250,36 @@ private: // Hypertext action, returns true if notification should close. // Action is stored in NotificationData::callback as std::function virtual bool on_text_click(); - + protected: const NotificationData m_data; - // For reusing ImGUI windows. NotificationIDProvider &m_id_provider; + int m_id{ 0 }; + // State for rendering EState m_state { EState::Unknown }; - int m_id { 0 }; - bool m_initialized { false }; + // Time values for rendering fade-out + + int64_t m_fading_start{ 0LL }; + + // first appereance of notification or last hover; + int64_t m_notification_start; + // time to next must-do render + int64_t m_next_render{ std::numeric_limits::max() }; + float m_current_fade_opacity{ 1.0f }; + + // Notification data + // Main text std::string m_text1; // Clickable text std::string m_hypertext; // Aditional text after hypertext - currently not used std::string m_text2; - // Countdown variables - long m_remaining_time; - bool m_counting_down; - long m_last_remaining_time; - bool m_paused { false }; - int m_countdown_frame { 0 }; - bool m_fading_out { false }; - int64_t m_fading_start { 0LL }; - // time of last done render when fading - int64_t m_last_render_fading { 0LL }; - // first appereance of notification or last hover; - int64_t m_notification_start; - // time to next must-do render - int64_t m_next_render { std::numeric_limits::max() }; - float m_current_fade_opacity { 1.0f }; - // If hidden the notif is alive but not visible to user - bool m_hidden { false }; - // m_finished = true - does not render, marked to delete - bool m_finished { false }; - // Will go to m_finished next render - bool m_close_pending { false }; - bool m_hovered { false }; - // variables to count positions correctly + + // inner variables to position notification window, texts and buttons correctly + // all space without text float m_window_width_offset; // Space on left side without text @@ -302,9 +289,7 @@ private: float m_window_width { 450.0f }; //Distance from bottom of notifications to top of this notification float m_top_y { 0.0f }; - - // Height of text - // Used as basic scaling unit! + // Height of text - Used as basic scaling unit! float m_line_height; std::vector m_endlines; // Gray are f.e. eorrors when its uknown if they are still valid @@ -344,6 +329,15 @@ private: int warning_step; }; + class PlaterWarningNotification : public PopNotification + { + public: + PlaterWarningNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler) : PopNotification(n, id_provider, evt_handler) {} + virtual void close() { m_state = EState::Hidden; } + void real_close() { m_state = EState::ClosePending; } + void show() { m_state = EState::Unknown; } + }; + class ProgressBarNotification : public PopNotification { public: @@ -405,33 +399,25 @@ private: void sort_notifications(); // If there is some error notification active, then the "Export G-code" notification after the slicing is finished is suppressed. bool has_slicing_error_notification(); - // perform update_state on each notification and ask for more frames if needed - void update_notifications(); - + // Target for wxWidgets events sent by clicking on the hyperlink available at some notifications. wxEvtHandler* m_evt_handler; // Cache of IDs to identify and reuse ImGUI windows. NotificationIDProvider m_id_provider; std::deque> m_pop_notifications; - // Last render time in seconds for fade out control. - long m_last_time { 0 }; - // When mouse hovers over some notification, the fade-out of all notifications is suppressed. - bool m_hovered { false }; //timestamps used for slicing finished - notification could be gone so it needs to be stored here std::unordered_set m_used_timestamps; // True if G-code preview is active. False if the Plater is active. bool m_in_preview { false }; // True if the layer editing is enabled in Plater, so that the notifications are shifted left of it. bool m_move_from_overlay { false }; + // Timestamp of last rendering + int64_t m_last_render { 0LL }; //prepared (basic) notifications const std::vector basic_notifications = { -// {NotificationType::SlicingNotPossible, NotificationLevel::RegularNotification, 10, _u8L("Slicing is not possible.")}, -// {NotificationType::ExportToRemovableFinished, NotificationLevel::ImportantNotification, 0, _u8L("Exporting finished."), _u8L("Eject drive.") }, {NotificationType::Mouse3dDisconnected, NotificationLevel::RegularNotification, 10, _u8L("3D Mouse disconnected.") }, -// {NotificationType::Mouse3dConnected, NotificationLevel::RegularNotification, 5, _u8L("3D Mouse connected.") }, -// {NotificationType::NewPresetsAviable, NotificationLevel::ImportantNotification, 20, _u8L("New Presets are available."), _u8L("See here.") }, - {NotificationType::PresetUpdateAvailable, NotificationLevel::ImportantNotification, 20, _u8L("Configuration update is available."), _u8L("See more."), + {NotificationType::PresetUpdateAvailable, NotificationLevel::ImportantNotification, 10, _u8L("Configuration update is available."), _u8L("See more."), [](wxEvtHandler* evnthndlr) { if (evnthndlr != nullptr) wxPostEvent(evnthndlr, PresetUpdateAvailableClickedEvent(EVT_PRESET_UPDATE_AVAILABLE_CLICKED)); From 32dd1f6e7c3e2ebbf9fc111d2b428a165c1227ee Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 8 Feb 2021 16:22:52 +0100 Subject: [PATCH 085/285] notification time correction --- src/slic3r/GUI/NotificationManager.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 4c584d3663..63492e61c4 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -417,7 +417,7 @@ private: //prepared (basic) notifications const std::vector basic_notifications = { {NotificationType::Mouse3dDisconnected, NotificationLevel::RegularNotification, 10, _u8L("3D Mouse disconnected.") }, - {NotificationType::PresetUpdateAvailable, NotificationLevel::ImportantNotification, 10, _u8L("Configuration update is available."), _u8L("See more."), + {NotificationType::PresetUpdateAvailable, NotificationLevel::ImportantNotification, 20, _u8L("Configuration update is available."), _u8L("See more."), [](wxEvtHandler* evnthndlr) { if (evnthndlr != nullptr) wxPostEvent(evnthndlr, PresetUpdateAvailableClickedEvent(EVT_PRESET_UPDATE_AVAILABLE_CLICKED)); From 80f0d305c18a5e562631d32592b19ff2f2d3d518 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 8 Feb 2021 17:42:20 +0100 Subject: [PATCH 086/285] request frame change in notification --- src/slic3r/GUI/NotificationManager.cpp | 38 ++++++++++++++++---------- src/slic3r/GUI/NotificationManager.hpp | 3 +- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 41c8e8f6fc..ab2e466500 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -547,15 +547,17 @@ bool NotificationManager::PopNotification::compare_text(const std::string& text) return false; } -void NotificationManager::PopNotification::update_state(bool paused, const int64_t delta) +bool NotificationManager::PopNotification::update_state(bool paused, const int64_t delta) { - if (m_state == EState::Unknown) + if (m_state == EState::Unknown) { init(); + return true; + } m_next_render = std::numeric_limits::max(); if (m_state == EState::Hidden) { - return; + return false; } int64_t now = GLCanvas3D::timestamp_now(); @@ -581,22 +583,25 @@ void NotificationManager::PopNotification::update_state(bool paused, const int64 m_current_fade_opacity = std::clamp(1.0f - 0.001f * static_cast(curr_time) / FADING_OUT_DURATION, 0.0f, 1.0f); if (m_current_fade_opacity <= 0.0f) m_state = EState::Finished; - else if (next_render < 0) - m_next_render = 0; + else if (next_render <= 20) { + m_next_render = FADING_OUT_TIMEOUT; + return true; + } else m_next_render = next_render; } if (m_state == EState::Finished) { - m_next_render = 0; - return; + //m_next_render = 0; + return true; } if (m_state == EState::ClosePending) { m_state = EState::Finished; - m_next_render = 0; - return; + //m_next_render = 0; + return true; } + return false; } NotificationManager::SlicingCompleteLargeNotification::SlicingCompleteLargeNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, bool large) : @@ -1034,6 +1039,7 @@ void NotificationManager::render_notifications(GLCanvas3D& canvas, float overlay } } + BOOST_LOG_TRIVIAL(error) << "render " << GLCanvas3D::timestamp_now() - m_last_render; m_last_render = GLCanvas3D::timestamp_now(); } @@ -1051,6 +1057,7 @@ bool NotificationManager::update_notifications(GLCanvas3D& canvas) const int64_t max = std::numeric_limits::max(); int64_t next_render = max; const int64_t time_since_render = GLCanvas3D::timestamp_now() - m_last_render; + bool request_render = false; // During render, each notification detects if its currently hovered and changes its state to EState::Hovered // If any notification is hovered, all restarts its countdown bool hover = false; @@ -1063,7 +1070,7 @@ bool NotificationManager::update_notifications(GLCanvas3D& canvas) // update state of all notif and erase finished for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end();) { std::unique_ptr& notification = *it; - notification->update_state(hover, time_since_render); + request_render |= notification->update_state(hover, time_since_render); next_render = std::min(next_render, notification->next_render()); if (notification->get_state() == PopNotification::EState::Finished) it = m_pop_notifications.erase(it); @@ -1071,16 +1078,19 @@ bool NotificationManager::update_notifications(GLCanvas3D& canvas) ++it; } + BOOST_LOG_TRIVIAL(error) << "update " << request_render << " : " << next_render <<" : " << GLCanvas3D::timestamp_now() - m_last_update; + m_last_update = GLCanvas3D::timestamp_now(); + //BOOST_LOG_TRIVIAL(error) << time_since_render << ":" << next_render; + // render needed right now - if (next_render < 20) - return true; + //if (next_render < 20) + // request_render = true; // request next frame if (next_render < max) canvas.schedule_extra_frame(int(next_render)); - return false; + return request_render; } ->>>>>>> 6df0d8ff81... Notifications management and rendering refactoring. void NotificationManager::sort_notifications() { diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 63492e61c4..0d1cd99966 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -222,7 +222,7 @@ private: bool compare_text(const std::string& text); void hide(bool h) { m_state = h ? EState::Hidden : EState::Unknown; } // sets m_next_render with time of next mandatory rendering. Delta is time since last render. - void update_state(bool paused, const int64_t delta); + bool update_state(bool paused, const int64_t delta); int64_t next_render() const { return is_finished() ? 0 : m_next_render; } EState get_state() const { return m_state; } bool is_hovered() const { return m_state == EState::Hovered; } @@ -413,6 +413,7 @@ private: bool m_move_from_overlay { false }; // Timestamp of last rendering int64_t m_last_render { 0LL }; + int64_t m_last_update { 0LL }; //prepared (basic) notifications const std::vector basic_notifications = { From bad12b5683015a58a17d634717143dec0adca0a1 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 8 Feb 2021 19:12:32 +0100 Subject: [PATCH 087/285] cleanup --- src/slic3r/GUI/NotificationManager.cpp | 5 ----- src/slic3r/GUI/NotificationManager.hpp | 1 - 2 files changed, 6 deletions(-) diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index ab2e466500..28feb65d05 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -1039,7 +1039,6 @@ void NotificationManager::render_notifications(GLCanvas3D& canvas, float overlay } } - BOOST_LOG_TRIVIAL(error) << "render " << GLCanvas3D::timestamp_now() - m_last_render; m_last_render = GLCanvas3D::timestamp_now(); } @@ -1078,10 +1077,6 @@ bool NotificationManager::update_notifications(GLCanvas3D& canvas) ++it; } - BOOST_LOG_TRIVIAL(error) << "update " << request_render << " : " << next_render <<" : " << GLCanvas3D::timestamp_now() - m_last_update; - m_last_update = GLCanvas3D::timestamp_now(); - //BOOST_LOG_TRIVIAL(error) << time_since_render << ":" << next_render; - // render needed right now //if (next_render < 20) // request_render = true; diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 0d1cd99966..768f941a7b 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -413,7 +413,6 @@ private: bool m_move_from_overlay { false }; // Timestamp of last rendering int64_t m_last_render { 0LL }; - int64_t m_last_update { 0LL }; //prepared (basic) notifications const std::vector basic_notifications = { From 6e325ee3221fe359ea951be3eae5f86adc3ff189 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 9 Feb 2021 09:19:59 +0100 Subject: [PATCH 088/285] cleanup --- src/slic3r/GUI/NotificationManager.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 28feb65d05..7e2a6b880e 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -549,13 +549,14 @@ bool NotificationManager::PopNotification::compare_text(const std::string& text) bool NotificationManager::PopNotification::update_state(bool paused, const int64_t delta) { + + m_next_render = std::numeric_limits::max(); + if (m_state == EState::Unknown) { init(); return true; } - m_next_render = std::numeric_limits::max(); - if (m_state == EState::Hidden) { return false; } @@ -581,24 +582,24 @@ bool NotificationManager::PopNotification::update_state(bool paused, const int64 int64_t curr_time = now - m_fading_start; int64_t next_render = FADING_OUT_TIMEOUT - delta; m_current_fade_opacity = std::clamp(1.0f - 0.001f * static_cast(curr_time) / FADING_OUT_DURATION, 0.0f, 1.0f); - if (m_current_fade_opacity <= 0.0f) + if (m_current_fade_opacity <= 0.0f) { m_state = EState::Finished; - else if (next_render <= 20) { + return true; + } else if (next_render <= 20) { m_next_render = FADING_OUT_TIMEOUT; return true; - } - else + } else { m_next_render = next_render; + return false; + } } if (m_state == EState::Finished) { - //m_next_render = 0; return true; } if (m_state == EState::ClosePending) { m_state = EState::Finished; - //m_next_render = 0; return true; } return false; @@ -1077,10 +1078,7 @@ bool NotificationManager::update_notifications(GLCanvas3D& canvas) ++it; } - // render needed right now - //if (next_render < 20) - // request_render = true; - // request next frame + // request next frame in future if (next_render < max) canvas.schedule_extra_frame(int(next_render)); From bf032524ebe8f767836875978c9f9938342c976a Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 3 Mar 2021 09:24:16 +0100 Subject: [PATCH 089/285] notifications - minor changes in logic --- src/slic3r/GUI/NotificationManager.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 7e2a6b880e..930174e3f5 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -568,12 +568,12 @@ bool NotificationManager::PopNotification::update_state(bool paused, const int64 m_current_fade_opacity = 1.0f; m_notification_start = now; // Timers when not fading - } else if (m_data.duration != 0 && !paused) { + } else if (m_state != EState::FadingOut && m_data.duration != 0 && !paused) { int64_t up_time = now - m_notification_start; - if (m_state != EState::FadingOut && up_time >= m_data.duration * 1000) { + if (up_time >= m_data.duration * 1000) { m_state = EState::FadingOut; m_fading_start = now; - } else if (m_state != EState::FadingOut) { + } else { m_next_render = m_data.duration * 1000 - up_time; } } From 6716492efa3c218da57a60c3937bb9777a20b25c Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 4 Mar 2021 21:56:07 +0100 Subject: [PATCH 090/285] Printhost upload progress bar notification --- src/slic3r/GUI/GUI_App.cpp | 5 ++ src/slic3r/GUI/GUI_App.hpp | 17 ++--- src/slic3r/GUI/NotificationManager.cpp | 87 +++++++++++++++++++++----- src/slic3r/GUI/NotificationManager.hpp | 37 +++++++++-- src/slic3r/GUI/PrintHostDialogs.cpp | 25 ++++++++ 5 files changed, 143 insertions(+), 28 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 2fde30cd13..ecc5f46a7c 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -1978,6 +1978,11 @@ wxNotebook* GUI_App::tab_panel() const return mainframe->m_tabpanel; } +NotificationManager* GUI_App::notification_manager() +{ + return plater_->get_notification_manager(); +} + // extruders count from selected printer preset int GUI_App::extruders_cnt() const { diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 5572c50712..f1ee0746a0 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -43,6 +43,7 @@ class ObjectSettings; class ObjectList; class ObjectLayers; class Plater; +class NotificationManager; struct GUI_InitParams; @@ -226,14 +227,14 @@ public: void MacOpenFiles(const wxArrayString &fileNames) override; #endif /* __APPLE */ - Sidebar& sidebar(); - ObjectManipulation* obj_manipul(); - ObjectSettings* obj_settings(); - ObjectList* obj_list(); - ObjectLayers* obj_layers(); - Plater* plater(); - Model& model(); - + Sidebar& sidebar(); + ObjectManipulation* obj_manipul(); + ObjectSettings* obj_settings(); + ObjectList* obj_list(); + ObjectLayers* obj_layers(); + Plater* plater(); + Model& model(); + NotificationManager* notification_manager(); // Parameters extracted from the command line to be passed to GUI after initialization. const GUI_InitParams* init_params { nullptr }; diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 930174e3f5..c9cf4d8cc9 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -359,12 +359,14 @@ void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, cons ImGui::SetCursorPosY(win_size.y / 2 - win_size.y / 6 - m_line_height / 2); imgui.text(m_text1.substr(0, m_endlines[0]).c_str()); // line2 - std::string line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0)); - cursor_y = win_size.y / 2 + win_size.y / 6 - m_line_height / 2; - ImGui::SetCursorPosX(x_offset); - ImGui::SetCursorPosY(cursor_y); - imgui.text(line.c_str()); - cursor_x = x_offset + ImGui::CalcTextSize(line.c_str()).x; + if (m_text1.length() > m_endlines[0]) { + std::string line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0)); + cursor_y = win_size.y / 2 + win_size.y / 6 - m_line_height / 2; + ImGui::SetCursorPosX(x_offset); + ImGui::SetCursorPosY(cursor_y); + imgui.text(line.c_str()); + cursor_x = x_offset + ImGui::CalcTextSize(line.c_str()).x; + } } else { ImGui::SetCursorPosX(x_offset); ImGui::SetCursorPosY(cursor_y); @@ -776,17 +778,52 @@ void NotificationManager::ProgressBarNotification::init() } void NotificationManager::ProgressBarNotification::render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) { - PopNotification::render_text(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y); + // line1 - we do not print any more text than what fits on line 1. Line 2 is bar. + ImGui::SetCursorPosX(m_left_indentation); + ImGui::SetCursorPosY(win_size_y / 2 - win_size_y / 6 - m_line_height / 2); + imgui.text(m_text1.substr(0, m_endlines[0]).c_str()); render_bar(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y); } void NotificationManager::ProgressBarNotification::render_bar(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) { - ImVec4 orange_color = ImVec4(.99f, .313f, .0f, 1.0f); - float invisible_length = 0; - - ImVec2 lineEnd = ImVec2(win_pos_x - invisible_length - m_window_width_offset, win_pos_y + win_size_y/2 + m_line_height / 2); - ImVec2 lineStart = ImVec2(win_pos_x - win_size_x + m_left_indentation, win_pos_y + win_size_y/2 + m_line_height / 2); - ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, IM_COL32((int)(orange_color.x * 255), (int)(orange_color.y * 255), (int)(orange_color.z * 255), (1.0f * 255.f)), m_line_height * 0.7f); + switch (m_pb_state) + { + case Slic3r::GUI::NotificationManager::ProgressBarNotification::ProgressBarState::PB_PROGRESS: + { + ImVec4 orange_color = ImVec4(.99f, .313f, .0f, 1.0f); + ImVec4 gray_color = ImVec4(.34f, .34f, .34f, 1.0f); + float invisible_length = 0; + ImVec2 lineEnd = ImVec2(win_pos_x - invisible_length - m_window_width_offset, win_pos_y + win_size_y / 2 + m_line_height / 2); + ImVec2 lineStart = ImVec2(win_pos_x - win_size_x + m_left_indentation, win_pos_y + win_size_y / 2 + m_line_height / 2); + float full_lenght = lineEnd.x - lineStart.x; + ImVec2 midPoint = ImVec2(lineStart.x + full_lenght * m_percentage, lineStart.y); + ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, IM_COL32((int)(gray_color.x * 255), (int)(gray_color.y * 255), (int)(gray_color.z * 255), (1.0f * 255.f)), m_line_height * 0.7f); + ImGui::GetWindowDrawList()->AddLine(lineStart, midPoint, IM_COL32((int)(orange_color.x * 255), (int)(orange_color.y * 255), (int)(orange_color.z * 255), (1.0f * 255.f)), m_line_height * 0.7f); + break; + } + case Slic3r::GUI::NotificationManager::ProgressBarNotification::ProgressBarState::PB_ERROR: + { + ImGui::SetCursorPosX(m_left_indentation); + ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - m_line_height / 2); + imgui.text(_u8L("ERROR")); + break; + } + case Slic3r::GUI::NotificationManager::ProgressBarNotification::ProgressBarState::PB_CANCELLED: + { + ImGui::SetCursorPosX(m_left_indentation); + ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - m_line_height / 2); + imgui.text(_u8L("CANCELED")); + break; + } + case Slic3r::GUI::NotificationManager::ProgressBarNotification::ProgressBarState::PB_COMPLETED: + { + ImGui::SetCursorPosX(m_left_indentation); + ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - m_line_height / 2); + imgui.text(_u8L("COMPLETED")); + break; + } + } + } //------NotificationManager-------- @@ -989,7 +1026,6 @@ void NotificationManager::set_progress_bar_percentage(const std::string& text, f for (std::unique_ptr& notification : m_pop_notifications) { if (notification->get_type() == NotificationType::ProgressBar && notification->compare_text(text)) { dynamic_cast(notification.get())->set_percentage(percentage); - // FIX ME: this is massive gpu eater (render every frame) wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); found = true; } @@ -998,6 +1034,26 @@ void NotificationManager::set_progress_bar_percentage(const std::string& text, f push_progress_bar_notification(text, percentage); } } +void NotificationManager::progress_bar_show_canceled(const std::string& text) +{ + for (std::unique_ptr& notification : m_pop_notifications) { + if (notification->get_type() == NotificationType::ProgressBar && notification->compare_text(text)) { + dynamic_cast(notification.get())->cancel(); + wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); + break; + } + } +} +void NotificationManager::progress_bar_show_error(const std::string& text) +{ + for (std::unique_ptr& notification : m_pop_notifications) { + if (notification->get_type() == NotificationType::ProgressBar && notification->compare_text(text)) { + dynamic_cast(notification.get())->error(); + wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); + break; + } + } +} bool NotificationManager::push_notification_data(const NotificationData& notification_data, int timestamp) { return push_notification_data(std::make_unique(notification_data, m_id_provider, m_evt_handler), timestamp); @@ -1103,7 +1159,8 @@ bool NotificationManager::activate_existing(const NotificationManager::PopNotifi const std::string &new_text = notification->get_data().text1; for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end(); ++it) { if ((*it)->get_type() == new_type && !(*it)->is_finished()) { - if (new_type == NotificationType::CustomNotification || new_type == NotificationType::PlaterWarning) { + if (std::find(m_multiple_types.begin(), m_multiple_types.end(), new_type) != m_multiple_types.end()) { + //if (new_type == NotificationType::CustomNotification || new_type == NotificationType::PlaterWarning || new_type == NotificationType::ProgressBar) { if (!(*it)->compare_text(new_text)) continue; } else if (new_type == NotificationType::SlicingWarning) { diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 768f941a7b..2baaf70546 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -142,6 +142,8 @@ public: // notification with progress bar void push_progress_bar_notification(const std::string& text, float percentage = 0); void set_progress_bar_percentage(const std::string& text, float percentage); + void progress_bar_show_canceled(const std::string& text); + void progress_bar_show_error(const std::string& text); // Close old notification ExportFinished. void new_export_began(bool on_removable); // finds ExportFinished notification and closes it if it was to removable device @@ -228,7 +230,7 @@ private: bool is_hovered() const { return m_state == EState::Hovered; } // Call after every size change - void init(); + virtual void init(); // Part of init() virtual void count_spaces(); // Calculetes correct size but not se it in imgui! @@ -341,8 +343,28 @@ private: class ProgressBarNotification : public PopNotification { public: + enum class ProgressBarState + { + PB_PROGRESS, + PB_ERROR, + PB_CANCELLED, + PB_COMPLETED + }; ProgressBarNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, float percentage) : PopNotification(n, id_provider, evt_handler) { set_percentage(percentage); } - void set_percentage(float percent) { m_percentage = percent; if (percent >= 1.0f) m_progress_complete = true; else m_progress_complete = false; } + void set_percentage(float percent) + { + if (m_pb_state == ProgressBarState::PB_CANCELLED) + return; + m_percentage = percent; + if (percent >= 1.0f) + m_pb_state = ProgressBarState::PB_COMPLETED; + else if (percent < 0.0f ) + m_pb_state = ProgressBarState::PB_ERROR; + else + m_pb_state = ProgressBarState::PB_PROGRESS; + } + void cancel() { m_pb_state = ProgressBarState::PB_CANCELLED; } + void error() { m_pb_state = ProgressBarState::PB_ERROR; } protected: virtual void init(); virtual void render_text(ImGuiWrapper& imgui, @@ -351,8 +373,12 @@ private: void render_bar(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y); - bool m_progress_complete{ false }; - float m_percentage; + float m_percentage; + ProgressBarState m_pb_state { ProgressBarState::PB_PROGRESS }; + }; + + class PrintHostUploadNotification : public ProgressBarNotification + { }; class ExportFinishedNotification : public PopNotification @@ -413,7 +439,8 @@ private: bool m_move_from_overlay { false }; // Timestamp of last rendering int64_t m_last_render { 0LL }; - + // Notification types that can be shown multiple types at once (compared by text) + const std::vector m_multiple_types = { NotificationType::CustomNotification, NotificationType::PlaterWarning, NotificationType::ProgressBar }; //prepared (basic) notifications const std::vector basic_notifications = { {NotificationType::Mouse3dDisconnected, NotificationLevel::RegularNotification, 10, _u8L("3D Mouse disconnected.") }, diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index a094b70e9a..921337d4a0 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -25,6 +25,7 @@ #include "wxExtensions.hpp" #include "MainFrame.hpp" #include "libslic3r/AppConfig.hpp" +#include "NotificationManager.hpp" namespace fs = boost::filesystem; @@ -280,6 +281,9 @@ void PrintHostQueueDialog::append_job(const PrintHostJob &job) job_list->AppendItem(fields, static_cast(ST_NEW)); // Both strings are UTF-8 encoded. upload_names.emplace_back(job.printhost->get_host(), job.upload_data.upload_path.string()); + + std::string notification_text = "[" + std::to_string(job_list->GetItemCount()) + "] " + job.upload_data.upload_path.string() + " -> " + job.printhost->get_host(); + wxGetApp().notification_manager()->push_progress_bar_notification(notification_text); } void PrintHostQueueDialog::on_dpi_changed(const wxRect &suggested_rect) @@ -345,6 +349,15 @@ void PrintHostQueueDialog::on_progress(Event &evt) } on_list_select(); + + if (evt.progress > 0) + { + wxVariant nm, hst; + job_list->GetValue(nm, evt.job_id, COL_FILENAME); + job_list->GetValue(hst, evt.job_id, COL_HOST); + std::string notification_text = "[" + std::to_string(evt.job_id + 1) + "] " + boost::nowide::narrow(nm.GetString()) + " -> " + boost::nowide::narrow(hst.GetString()); + wxGetApp().notification_manager()->set_progress_bar_percentage(notification_text, 100 / evt.progress); + } } void PrintHostQueueDialog::on_error(Event &evt) @@ -360,6 +373,12 @@ void PrintHostQueueDialog::on_error(Event &evt) on_list_select(); GUI::show_error(nullptr, errormsg); + + wxVariant nm, hst; + job_list->GetValue(nm, evt.job_id, COL_FILENAME); + job_list->GetValue(hst, evt.job_id, COL_HOST); + std::string notification_text = "[" + std::to_string(evt.job_id + 1) + "] " + boost::nowide::narrow(nm.GetString()) + " -> " + boost::nowide::narrow(hst.GetString()); + wxGetApp().notification_manager()->progress_bar_show_error(notification_text); } void PrintHostQueueDialog::on_cancel(Event &evt) @@ -370,6 +389,12 @@ void PrintHostQueueDialog::on_cancel(Event &evt) job_list->SetValue(wxVariant(0), evt.job_id, COL_PROGRESS); on_list_select(); + + wxVariant nm, hst; + job_list->GetValue(nm, evt.job_id, COL_FILENAME); + job_list->GetValue(hst, evt.job_id, COL_HOST); + std::string notification_text = "[" + std::to_string(evt.job_id + 1) + "] " + boost::nowide::narrow(nm.GetString()) + " -> " + boost::nowide::narrow(hst.GetString()); + wxGetApp().notification_manager()->progress_bar_show_canceled(notification_text); } void PrintHostQueueDialog::get_active_jobs(std::vector>& ret) From 44bfb914ab03d1e77ecbc50eeb7865dc76351150 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Sat, 6 Mar 2021 11:26:29 +0100 Subject: [PATCH 091/285] progress bar notification - percentage text --- src/slic3r/GUI/NotificationManager.cpp | 39 +++++++++----------------- 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index c9cf4d8cc9..a5777cd552 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -786,44 +786,33 @@ void NotificationManager::ProgressBarNotification::render_text(ImGuiWrapper& img } void NotificationManager::ProgressBarNotification::render_bar(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) { - switch (m_pb_state) - { + std::string text; + switch (m_pb_state) { case Slic3r::GUI::NotificationManager::ProgressBarNotification::ProgressBarState::PB_PROGRESS: { - ImVec4 orange_color = ImVec4(.99f, .313f, .0f, 1.0f); - ImVec4 gray_color = ImVec4(.34f, .34f, .34f, 1.0f); - float invisible_length = 0; - ImVec2 lineEnd = ImVec2(win_pos_x - invisible_length - m_window_width_offset, win_pos_y + win_size_y / 2 + m_line_height / 2); - ImVec2 lineStart = ImVec2(win_pos_x - win_size_x + m_left_indentation, win_pos_y + win_size_y / 2 + m_line_height / 2); - float full_lenght = lineEnd.x - lineStart.x; - ImVec2 midPoint = ImVec2(lineStart.x + full_lenght * m_percentage, lineStart.y); + text = std::to_string((int)(m_percentage * 100)) + "%"; + ImVec4 orange_color = ImVec4(.99f, .313f, .0f, 1.0f); + ImVec4 gray_color = ImVec4(.34f, .34f, .34f, 1.0f); + ImVec2 lineEnd = ImVec2(win_pos_x - m_window_width_offset, win_pos_y + win_size_y / 2 + m_line_height / 2); + ImVec2 lineStart = ImVec2(win_pos_x - win_size_x + m_left_indentation + ImGui::CalcTextSize(text.c_str()).x, win_pos_y + win_size_y / 2 + m_line_height / 2); + ImVec2 midPoint = ImVec2(lineStart.x + (lineEnd.x - lineStart.x) * m_percentage, lineStart.y); ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, IM_COL32((int)(gray_color.x * 255), (int)(gray_color.y * 255), (int)(gray_color.z * 255), (1.0f * 255.f)), m_line_height * 0.7f); ImGui::GetWindowDrawList()->AddLine(lineStart, midPoint, IM_COL32((int)(orange_color.x * 255), (int)(orange_color.y * 255), (int)(orange_color.z * 255), (1.0f * 255.f)), m_line_height * 0.7f); break; } case Slic3r::GUI::NotificationManager::ProgressBarNotification::ProgressBarState::PB_ERROR: - { - ImGui::SetCursorPosX(m_left_indentation); - ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - m_line_height / 2); - imgui.text(_u8L("ERROR")); + text = _u8L("ERROR"); break; - } case Slic3r::GUI::NotificationManager::ProgressBarNotification::ProgressBarState::PB_CANCELLED: - { - ImGui::SetCursorPosX(m_left_indentation); - ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - m_line_height / 2); - imgui.text(_u8L("CANCELED")); + text = _u8L("CANCELED"); break; - } case Slic3r::GUI::NotificationManager::ProgressBarNotification::ProgressBarState::PB_COMPLETED: - { - ImGui::SetCursorPosX(m_left_indentation); - ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - m_line_height / 2); - imgui.text(_u8L("COMPLETED")); + text = _u8L("COMPLETED"); break; } - } - + ImGui::SetCursorPosX(m_left_indentation); + ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - m_line_height / 2); + imgui.text(text.c_str()); } //------NotificationManager-------- From 62c2095fe8297688c254ef70d9742f18fffe1b77 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 11 Mar 2021 13:33:31 +0100 Subject: [PATCH 092/285] Print host upload notification with more info and cancel button --- resources/icons/notification_cancel.svg | 67 +++++++ resources/icons/notification_cancel_hover.svg | 67 +++++++ src/imgui/imconfig.h | 2 + src/slic3r/GUI/ImGuiWrapper.cpp | 4 +- src/slic3r/GUI/NotificationManager.cpp | 175 +++++++++++++++--- src/slic3r/GUI/NotificationManager.hpp | 92 +++++---- src/slic3r/GUI/PrintHostDialogs.cpp | 12 +- 7 files changed, 350 insertions(+), 69 deletions(-) create mode 100644 resources/icons/notification_cancel.svg create mode 100644 resources/icons/notification_cancel_hover.svg diff --git a/resources/icons/notification_cancel.svg b/resources/icons/notification_cancel.svg new file mode 100644 index 0000000000..d849e24c61 --- /dev/null +++ b/resources/icons/notification_cancel.svg @@ -0,0 +1,67 @@ + +image/svg+xml + + + + + + + diff --git a/resources/icons/notification_cancel_hover.svg b/resources/icons/notification_cancel_hover.svg new file mode 100644 index 0000000000..746d053e48 --- /dev/null +++ b/resources/icons/notification_cancel_hover.svg @@ -0,0 +1,67 @@ + +image/svg+xml + + + + + + + diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h index d52294acd2..1ee719288c 100644 --- a/src/imgui/imconfig.h +++ b/src/imgui/imconfig.h @@ -123,6 +123,8 @@ namespace ImGui const char ErrorMarker = 0x11; const char EjectButton = 0x12; const char EjectHoverButton = 0x13; + const char CancelButton = 0x14; + const char CancelHoverButton = 0x15; // void MyFunction(const char* name, const MyMatrix44& v); } diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 1ed4b492fd..db7af046bf 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -51,7 +51,9 @@ static const std::map font_icons_large = { {ImGui::EjectButton , "notification_eject_sd" }, {ImGui::EjectHoverButton , "notification_eject_sd_hover" }, {ImGui::WarningMarker , "notification_warning" }, - {ImGui::ErrorMarker , "notification_error" } + {ImGui::ErrorMarker , "notification_error" }, + {ImGui::CancelButton , "notification_cancel" }, + {ImGui::CancelHoverButton , "notification_cancel_hover" }, }; const ImVec4 ImGuiWrapper::COL_GREY_DARK = { 0.333f, 0.333f, 0.333f, 1.0f }; diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index a5777cd552..21df0e86b9 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -5,6 +5,7 @@ #include "Plater.hpp" #include "GLCanvas3D.hpp" #include "ImGuiWrapper.hpp" +#include "PrintHostDialogs.hpp" #include "wxExtensions.hpp" @@ -776,44 +777,157 @@ void NotificationManager::ProgressBarNotification::init() m_lines_count++; m_endlines.push_back(m_endlines.back()); } +void NotificationManager::ProgressBarNotification::count_spaces() +{ + //determine line width + m_line_height = ImGui::CalcTextSize("A").y; + + m_left_indentation = m_line_height; + if (m_data.level == NotificationLevel::ErrorNotification || m_data.level == NotificationLevel::WarningNotification) { + std::string text; + text = (m_data.level == NotificationLevel::ErrorNotification ? ImGui::ErrorMarker : ImGui::WarningMarker); + float picture_width = ImGui::CalcTextSize(text.c_str()).x; + m_left_indentation = picture_width + m_line_height / 2; + } + m_window_width_offset = m_line_height * (m_has_cancel_button ? 6 : 4); + m_window_width = m_line_height * 25; +} + void NotificationManager::ProgressBarNotification::render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) { // line1 - we do not print any more text than what fits on line 1. Line 2 is bar. ImGui::SetCursorPosX(m_left_indentation); ImGui::SetCursorPosY(win_size_y / 2 - win_size_y / 6 - m_line_height / 2); imgui.text(m_text1.substr(0, m_endlines[0]).c_str()); + if (m_has_cancel_button) + render_cancel_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y); render_bar(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y); + } void NotificationManager::ProgressBarNotification::render_bar(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) +{ + ImVec4 orange_color = ImVec4(.99f, .313f, .0f, 1.0f); + ImVec4 gray_color = ImVec4(.34f, .34f, .34f, 1.0f); + ImVec2 lineEnd = ImVec2(win_pos_x - m_window_width_offset, win_pos_y + win_size_y / 2 + m_line_height / 4); + ImVec2 lineStart = ImVec2(win_pos_x - win_size_x + m_left_indentation, win_pos_y + win_size_y / 2 + m_line_height / 4); + ImVec2 midPoint = ImVec2(lineStart.x + (lineEnd.x - lineStart.x) * m_percentage, lineStart.y); + ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, IM_COL32((int)(gray_color.x * 255), (int)(gray_color.y * 255), (int)(gray_color.z * 255), (1.0f * 255.f)), m_line_height * 0.2f); + ImGui::GetWindowDrawList()->AddLine(lineStart, midPoint, IM_COL32((int)(orange_color.x * 255), (int)(orange_color.y * 255), (int)(orange_color.z * 255), (1.0f * 255.f)), m_line_height * 0.2f); +} +//------PrintHostUploadNotification---------------- +void NotificationManager::PrintHostUploadNotification::set_percentage(float percent) +{ + if (m_uj_state == UploadJobState::PB_CANCELLED) + return; + m_percentage = percent; + if (percent >= 1.0f) { + m_uj_state = UploadJobState::PB_COMPLETED; + m_has_cancel_button = false; + } else if (percent < 0.0f) { + error(); + } else { + m_uj_state = UploadJobState::PB_PROGRESS; + m_has_cancel_button = true; + } +} +void NotificationManager::PrintHostUploadNotification::render_bar(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) { std::string text; - switch (m_pb_state) { - case Slic3r::GUI::NotificationManager::ProgressBarNotification::ProgressBarState::PB_PROGRESS: + switch (m_uj_state) { + case Slic3r::GUI::NotificationManager::PrintHostUploadNotification::UploadJobState::PB_PROGRESS: { - text = std::to_string((int)(m_percentage * 100)) + "%"; - ImVec4 orange_color = ImVec4(.99f, .313f, .0f, 1.0f); - ImVec4 gray_color = ImVec4(.34f, .34f, .34f, 1.0f); - ImVec2 lineEnd = ImVec2(win_pos_x - m_window_width_offset, win_pos_y + win_size_y / 2 + m_line_height / 2); - ImVec2 lineStart = ImVec2(win_pos_x - win_size_x + m_left_indentation + ImGui::CalcTextSize(text.c_str()).x, win_pos_y + win_size_y / 2 + m_line_height / 2); - ImVec2 midPoint = ImVec2(lineStart.x + (lineEnd.x - lineStart.x) * m_percentage, lineStart.y); - ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, IM_COL32((int)(gray_color.x * 255), (int)(gray_color.y * 255), (int)(gray_color.z * 255), (1.0f * 255.f)), m_line_height * 0.7f); - ImGui::GetWindowDrawList()->AddLine(lineStart, midPoint, IM_COL32((int)(orange_color.x * 255), (int)(orange_color.y * 255), (int)(orange_color.z * 255), (1.0f * 255.f)), m_line_height * 0.7f); + ProgressBarNotification::render_bar(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y); + float uploaded = m_file_size / 100 * m_percentage; + std::stringstream stream; + stream << std::fixed << std::setprecision(3) << (int)(m_percentage * 100) << "% - " << uploaded << " of " << m_file_size << "MB uploaded"; + text = stream.str(); + ImGui::SetCursorPosX(m_left_indentation); + ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 /*- m_line_height / 4 * 3*/); break; } - case Slic3r::GUI::NotificationManager::ProgressBarNotification::ProgressBarState::PB_ERROR: + case Slic3r::GUI::NotificationManager::PrintHostUploadNotification::UploadJobState::PB_ERROR: text = _u8L("ERROR"); + ImGui::SetCursorPosX(m_left_indentation); + ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - m_line_height / 2); break; - case Slic3r::GUI::NotificationManager::ProgressBarNotification::ProgressBarState::PB_CANCELLED: + case Slic3r::GUI::NotificationManager::PrintHostUploadNotification::UploadJobState::PB_CANCELLED: text = _u8L("CANCELED"); + ImGui::SetCursorPosX(m_left_indentation); + ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - m_line_height / 2); break; - case Slic3r::GUI::NotificationManager::ProgressBarNotification::ProgressBarState::PB_COMPLETED: + case Slic3r::GUI::NotificationManager::PrintHostUploadNotification::UploadJobState::PB_COMPLETED: text = _u8L("COMPLETED"); + ImGui::SetCursorPosX(m_left_indentation); + ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - m_line_height / 2); break; } - ImGui::SetCursorPosX(m_left_indentation); - ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - m_line_height / 2); + imgui.text(text.c_str()); +} +void NotificationManager::PrintHostUploadNotification::render_cancel_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) +{ + ImVec2 win_size(win_size_x, win_size_y); + ImVec2 win_pos(win_pos_x, win_pos_y); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f)); + Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity); + Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f)); + + std::string button_text; + button_text = ImGui::CancelButton; + + if (ImGui::IsMouseHoveringRect(ImVec2(win_pos.x - m_line_height * 5.f, win_pos.y), + ImVec2(win_pos.x - m_line_height * 2.5f, win_pos.y + win_size.y), + true)) + { + button_text = ImGui::CancelHoverButton; + // tooltip + long time_now = wxGetLocalTime(); + if (m_hover_time > 0 && m_hover_time < time_now) { + ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND); + ImGui::BeginTooltip(); + imgui.text(_u8L("Cancel upload") + " " + GUI::shortkey_ctrl_prefix() + "T"); + ImGui::EndTooltip(); + ImGui::PopStyleColor(); + } + if (m_hover_time == 0) + m_hover_time = time_now; + } + else + m_hover_time = 0; + + ImVec2 button_pic_size = ImGui::CalcTextSize(button_text.c_str()); + ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f); + ImGui::SetCursorPosX(win_size.x - m_line_height * 5.0f); + ImGui::SetCursorPosY(win_size.y / 2 - button_size.y); + if (imgui.button(button_text.c_str(), button_size.x, button_size.y)) + { + assert(m_evt_handler != nullptr); + if (m_evt_handler != nullptr) { + auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_CANCEL, m_job_id, m_job_id); + wxQueueEvent(m_evt_handler, evt); + } + } + + //invisible large button + ImGui::SetCursorPosX(win_size.x - m_line_height * 4.625f); + ImGui::SetCursorPosY(0); + if (imgui.button(" ", m_line_height * 2.f, win_size.y)) + { + assert(m_evt_handler != nullptr); + if (m_evt_handler != nullptr) { + auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_CANCEL, m_job_id, m_job_id); + wxQueueEvent(m_evt_handler, evt); + } + } + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + } //------NotificationManager-------- NotificationManager::NotificationManager(wxEvtHandler* evt_handler) : @@ -1004,40 +1118,47 @@ void NotificationManager::push_exporting_finished_notification(const std::string NotificationData data{ NotificationType::ExportFinished, NotificationLevel::RegularNotification, on_removable ? 0 : 20, _u8L("Exporting finished.") + "\n" + path }; push_notification_data(std::make_unique(data, m_id_provider, m_evt_handler, on_removable, path, dir_path), 0); } -void NotificationManager::push_progress_bar_notification(const std::string& text, float percentage) + +void NotificationManager::push_upload_job_notification(wxEvtHandler* evt_handler, int id, float filesize, const std::string& filename, const std::string& host, float percentage) { - NotificationData data{ NotificationType::ProgressBar, NotificationLevel::ProgressBarNotification, 0, text }; - push_notification_data(std::make_unique(data, m_id_provider, m_evt_handler, 0), 0); + std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host); + NotificationData data{ NotificationType::PrintHostUpload, NotificationLevel::ProgressBarNotification, 0, text }; + push_notification_data(std::make_unique(data, m_id_provider, evt_handler, 0, id, filesize), 0); } -void NotificationManager::set_progress_bar_percentage(const std::string& text, float percentage) +void NotificationManager::set_upload_job_notification_percentage(int id, const std::string& filename, const std::string& host, float percentage) { + std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host); bool found = false; for (std::unique_ptr& notification : m_pop_notifications) { if (notification->get_type() == NotificationType::ProgressBar && notification->compare_text(text)) { - dynamic_cast(notification.get())->set_percentage(percentage); + dynamic_cast(notification.get())->set_percentage(percentage); wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); found = true; } } + /* if (!found) { - push_progress_bar_notification(text, percentage); + push_upload_job_notification(id, filename, host, percentage); } + */ } -void NotificationManager::progress_bar_show_canceled(const std::string& text) +void NotificationManager::upload_job_notification_show_canceled(int id, const std::string& filename, const std::string& host) { + std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host); for (std::unique_ptr& notification : m_pop_notifications) { - if (notification->get_type() == NotificationType::ProgressBar && notification->compare_text(text)) { - dynamic_cast(notification.get())->cancel(); + if (notification->get_type() == NotificationType::PrintHostUpload && notification->compare_text(text)) { + dynamic_cast(notification.get())->cancel(); wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); break; } } } -void NotificationManager::progress_bar_show_error(const std::string& text) +void NotificationManager::upload_job_notification_show_error(int id, const std::string& filename, const std::string& host) { + std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host); for (std::unique_ptr& notification : m_pop_notifications) { - if (notification->get_type() == NotificationType::ProgressBar && notification->compare_text(text)) { - dynamic_cast(notification.get())->error(); + if (notification->get_type() == NotificationType::PrintHostUpload && notification->compare_text(text)) { + dynamic_cast(notification.get())->error(); wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); break; } diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 2baaf70546..4b32a716ff 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -66,6 +66,8 @@ enum class NotificationType PlaterWarning, // Progress bar instead of text. ProgressBar, + // Progress bar with info from Print Host Upload Queue dialog. + PrintHostUpload, // Notification, when Color Change G-code is empty and user try to add color change on DoubleSlider. EmptyColorChangeCode, // Notification that custom supports/seams were deleted after mesh repair. @@ -140,10 +142,10 @@ public: // Exporting finished, show this information with path, button to open containing folder and if ejectable - eject button void push_exporting_finished_notification(const std::string& path, const std::string& dir_path, bool on_removable); // notification with progress bar - void push_progress_bar_notification(const std::string& text, float percentage = 0); - void set_progress_bar_percentage(const std::string& text, float percentage); - void progress_bar_show_canceled(const std::string& text); - void progress_bar_show_error(const std::string& text); + void push_upload_job_notification(wxEvtHandler* evt_handler, int id, float filesize, const std::string& filename, const std::string& host, float percentage = 0); + void set_upload_job_notification_percentage(int id, const std::string& filename, const std::string& host, float percentage); + void upload_job_notification_show_canceled(int id, const std::string& filename, const std::string& host); + void upload_job_notification_show_error(int id, const std::string& filename, const std::string& host); // Close old notification ExportFinished. void new_export_began(bool on_removable); // finds ExportFinished notification and closes it if it was to removable device @@ -340,45 +342,69 @@ private: void show() { m_state = EState::Unknown; } }; + class ProgressBarNotification : public PopNotification { public: - enum class ProgressBarState - { - PB_PROGRESS, - PB_ERROR, - PB_CANCELLED, - PB_COMPLETED - }; + ProgressBarNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, float percentage) : PopNotification(n, id_provider, evt_handler) { set_percentage(percentage); } - void set_percentage(float percent) - { - if (m_pb_state == ProgressBarState::PB_CANCELLED) - return; - m_percentage = percent; - if (percent >= 1.0f) - m_pb_state = ProgressBarState::PB_COMPLETED; - else if (percent < 0.0f ) - m_pb_state = ProgressBarState::PB_ERROR; - else - m_pb_state = ProgressBarState::PB_PROGRESS; - } - void cancel() { m_pb_state = ProgressBarState::PB_CANCELLED; } - void error() { m_pb_state = ProgressBarState::PB_ERROR; } + virtual void set_percentage(float percent) { m_percentage = percent; } protected: - virtual void init(); + virtual void init() override; + virtual void count_spaces() override; virtual void render_text(ImGuiWrapper& imgui, - const float win_size_x, const float win_size_y, - const float win_pos_x, const float win_pos_y); - void render_bar(ImGuiWrapper& imgui, - const float win_size_x, const float win_size_y, - const float win_pos_x, const float win_pos_y); + const float win_size_x, const float win_size_y, + const float win_pos_x, const float win_pos_y); + virtual void render_bar(ImGuiWrapper& imgui, + const float win_size_x, const float win_size_y, + const float win_pos_x, const float win_pos_y); + virtual void render_cancel_button(ImGuiWrapper& imgui, + const float win_size_x, const float win_size_y, + const float win_pos_x, const float win_pos_y) + {} float m_percentage; - ProgressBarState m_pb_state { ProgressBarState::PB_PROGRESS }; + + bool m_has_cancel_button {false}; + // local time of last hover for showing tooltip + }; + + class PrintHostUploadNotification : public ProgressBarNotification { + public: + enum class UploadJobState + { + PB_PROGRESS, + PB_ERROR, + PB_CANCELLED, + PB_COMPLETED + }; + PrintHostUploadNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, float percentage, int job_id, float filesize) + :ProgressBarNotification(n, id_provider, evt_handler, percentage) + , m_job_id(job_id) + , m_file_size(filesize) + { + m_has_cancel_button = true; + } + static std::string get_upload_job_text(int id, const std::string& filename, const std::string& host) { return "[" + std::to_string(id) + "] " + filename + " -> " + host; } + virtual void set_percentage(float percent); + void cancel() { m_uj_state = UploadJobState::PB_CANCELLED; m_has_cancel_button = false; } + void error() { m_uj_state = UploadJobState::PB_ERROR; m_has_cancel_button = false; } + protected: + virtual void render_bar(ImGuiWrapper& imgui, + const float win_size_x, const float win_size_y, + const float win_pos_x, const float win_pos_y); + virtual void render_cancel_button(ImGuiWrapper& imgui, + const float win_size_x, const float win_size_y, + const float win_pos_x, const float win_pos_y); + // Identifies job in cancel callback + int m_job_id; + // Size of uploaded size to be displayed in MB + float m_file_size; + long m_hover_time{ 0 }; + UploadJobState m_uj_state{ UploadJobState::PB_PROGRESS }; }; class ExportFinishedNotification : public PopNotification @@ -440,7 +466,7 @@ private: // Timestamp of last rendering int64_t m_last_render { 0LL }; // Notification types that can be shown multiple types at once (compared by text) - const std::vector m_multiple_types = { NotificationType::CustomNotification, NotificationType::PlaterWarning, NotificationType::ProgressBar }; + const std::vector m_multiple_types = { NotificationType::CustomNotification, NotificationType::PlaterWarning, NotificationType::ProgressBar, NotificationType::PrintHostUpload }; //prepared (basic) notifications const std::vector basic_notifications = { {NotificationType::Mouse3dDisconnected, NotificationLevel::RegularNotification, 10, _u8L("3D Mouse disconnected.") }, diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index 921337d4a0..f3a1fae98a 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -282,8 +282,7 @@ void PrintHostQueueDialog::append_job(const PrintHostJob &job) // Both strings are UTF-8 encoded. upload_names.emplace_back(job.printhost->get_host(), job.upload_data.upload_path.string()); - std::string notification_text = "[" + std::to_string(job_list->GetItemCount()) + "] " + job.upload_data.upload_path.string() + " -> " + job.printhost->get_host(); - wxGetApp().notification_manager()->push_progress_bar_notification(notification_text); + wxGetApp().notification_manager()->push_upload_job_notification(this, job_list->GetItemCount(), 2.64931f, job.upload_data.upload_path.string(), job.printhost->get_host()); } void PrintHostQueueDialog::on_dpi_changed(const wxRect &suggested_rect) @@ -355,8 +354,7 @@ void PrintHostQueueDialog::on_progress(Event &evt) wxVariant nm, hst; job_list->GetValue(nm, evt.job_id, COL_FILENAME); job_list->GetValue(hst, evt.job_id, COL_HOST); - std::string notification_text = "[" + std::to_string(evt.job_id + 1) + "] " + boost::nowide::narrow(nm.GetString()) + " -> " + boost::nowide::narrow(hst.GetString()); - wxGetApp().notification_manager()->set_progress_bar_percentage(notification_text, 100 / evt.progress); + wxGetApp().notification_manager()->set_upload_job_notification_percentage(evt.job_id + 1, boost::nowide::narrow(nm.GetString()), boost::nowide::narrow(hst.GetString()), 100 / evt.progress); } } @@ -377,8 +375,7 @@ void PrintHostQueueDialog::on_error(Event &evt) wxVariant nm, hst; job_list->GetValue(nm, evt.job_id, COL_FILENAME); job_list->GetValue(hst, evt.job_id, COL_HOST); - std::string notification_text = "[" + std::to_string(evt.job_id + 1) + "] " + boost::nowide::narrow(nm.GetString()) + " -> " + boost::nowide::narrow(hst.GetString()); - wxGetApp().notification_manager()->progress_bar_show_error(notification_text); + wxGetApp().notification_manager()->upload_job_notification_show_error(evt.job_id + 1, boost::nowide::narrow(nm.GetString()), boost::nowide::narrow(hst.GetString())); } void PrintHostQueueDialog::on_cancel(Event &evt) @@ -393,8 +390,7 @@ void PrintHostQueueDialog::on_cancel(Event &evt) wxVariant nm, hst; job_list->GetValue(nm, evt.job_id, COL_FILENAME); job_list->GetValue(hst, evt.job_id, COL_HOST); - std::string notification_text = "[" + std::to_string(evt.job_id + 1) + "] " + boost::nowide::narrow(nm.GetString()) + " -> " + boost::nowide::narrow(hst.GetString()); - wxGetApp().notification_manager()->progress_bar_show_canceled(notification_text); + wxGetApp().notification_manager()->upload_job_notification_show_canceled(evt.job_id + 1, boost::nowide::narrow(nm.GetString()), boost::nowide::narrow(hst.GetString())); } void PrintHostQueueDialog::get_active_jobs(std::vector>& ret) From 15765eb99b65a82abc172bc065ca84dfa7423ed8 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Fri, 12 Mar 2021 10:19:13 +0100 Subject: [PATCH 093/285] Commented Print host upload notification until its tested --- src/slic3r/GUI/NotificationManager.cpp | 2 +- src/slic3r/GUI/PrintHostDialogs.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 21df0e86b9..266814e09c 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -839,7 +839,7 @@ void NotificationManager::PrintHostUploadNotification::render_bar(ImGuiWrapper& ProgressBarNotification::render_bar(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y); float uploaded = m_file_size / 100 * m_percentage; std::stringstream stream; - stream << std::fixed << std::setprecision(3) << (int)(m_percentage * 100) << "% - " << uploaded << " of " << m_file_size << "MB uploaded"; + stream << std::fixed << std::setprecision(2) << (int)(m_percentage * 100) << "% - " << uploaded << " of " << m_file_size << "MB uploaded"; text = stream.str(); ImGui::SetCursorPosX(m_left_indentation); ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 /*- m_line_height / 4 * 3*/); diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index f3a1fae98a..c8df141e9f 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -282,7 +282,7 @@ void PrintHostQueueDialog::append_job(const PrintHostJob &job) // Both strings are UTF-8 encoded. upload_names.emplace_back(job.printhost->get_host(), job.upload_data.upload_path.string()); - wxGetApp().notification_manager()->push_upload_job_notification(this, job_list->GetItemCount(), 2.64931f, job.upload_data.upload_path.string(), job.printhost->get_host()); + //wxGetApp().notification_manager()->push_upload_job_notification(this, job_list->GetItemCount(), 0, job.upload_data.upload_path.string(), job.printhost->get_host()); } void PrintHostQueueDialog::on_dpi_changed(const wxRect &suggested_rect) From c18ad5f9d691593b691033fa9beec9aba7682e09 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 12 Mar 2021 11:30:06 +0100 Subject: [PATCH 094/285] Added a missing include (gcc) Removed several includes, hopefully they're not needed on any other platform. --- src/slic3r/GUI/PrintHostDialogs.cpp | 2 +- src/slic3r/GUI/PrintHostDialogs.hpp | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index c8df141e9f..598b72b940 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -16,13 +16,13 @@ #include #include +#include #include "GUI.hpp" #include "GUI_App.hpp" #include "MsgDialog.hpp" #include "I18N.hpp" #include "../Utils/PrintHost.hpp" -#include "wxExtensions.hpp" #include "MainFrame.hpp" #include "libslic3r/AppConfig.hpp" #include "NotificationManager.hpp" diff --git a/src/slic3r/GUI/PrintHostDialogs.hpp b/src/slic3r/GUI/PrintHostDialogs.hpp index c185f57be2..294593bd1c 100644 --- a/src/slic3r/GUI/PrintHostDialogs.hpp +++ b/src/slic3r/GUI/PrintHostDialogs.hpp @@ -8,10 +8,8 @@ #include #include -#include "GUI.hpp" #include "GUI_Utils.hpp" #include "MsgDialog.hpp" -#include "../Utils/PrintHost.hpp" class wxButton; class wxTextCtrl; From 5f6253390fef6f3cfe0f19422723a811e081d5df Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 12 Mar 2021 16:26:34 +0100 Subject: [PATCH 095/285] Implemented suggestion of the auto color change, if model looks like sign --- src/slic3r/GUI/DoubleSlider.cpp | 2 ++ src/slic3r/GUI/GUI_Preview.cpp | 49 ++++++++++++++++++++++++++ src/slic3r/GUI/NotificationManager.cpp | 4 ++- src/slic3r/GUI/NotificationManager.hpp | 2 ++ 4 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 81945061b0..00b9c2e290 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -1930,6 +1930,8 @@ void Control::auto_color_change() double delta_area = scale_(scale_(25)); // equal to 25 mm2 for (auto object : print.objects()) { + if (object->layer_count() == 0) + continue; double prev_area = area(object->get_layer(0)->lslices); for (size_t i = 1; i < object->layers().size(); i++) { diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 2f11d66f5f..da7d7810ad 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -1,4 +1,5 @@ #include "libslic3r/libslic3r.h" +#include "libslic3r/Layer.hpp" #include "GUI_Preview.hpp" #include "GUI_App.hpp" #include "GUI.hpp" @@ -24,6 +25,7 @@ // this include must follow the wxWidgets ones or it won't compile on Windows -> see http://trac.wxwidgets.org/ticket/2421 #include "libslic3r/Print.hpp" #include "libslic3r/SLAPrint.hpp" +#include "NotificationManager.hpp" namespace Slic3r { namespace GUI { @@ -639,6 +641,53 @@ void Preview::update_layers_slider(const std::vector& layers_z, bool kee else m_layers_slider->SetLayersTimes(m_gcode_result->time_statistics.modes.front().layers_times); + // Suggest the auto color change, if model looks like sign + if (ticks_info_from_model.gcodes.empty()) + { + NotificationManager* notif_mngr = wxGetApp().plater()->get_notification_manager(); +// notif_mngr->close_notification_of_type(NotificationType::SignDetected); + + const Print& print = wxGetApp().plater()->fff_print(); + double delta_area = scale_(scale_(25)); // equal to 25 mm2 + + //bool is_possible_auto_color_change = false; + for (auto object : print.objects()) { + double height = object->height(); + coord_t longer_side = std::max(object->size().x(), object->size().y()); + if (height / longer_side > 0.3) + continue; + + const ExPolygons& bottom = object->get_layer(0)->lslices; + if (bottom.size() > 1 || !bottom[0].holes.empty()) + continue; + + double bottom_area = area(bottom); + int i; + for (i = 1; i < int(0.3 * object->layers().size()); i++) + if (area(object->get_layer(1)->lslices) != bottom_area) + break; + if (i < int(0.3 * object->layers().size())) + continue; + + double top_area = area(object->get_layer(int(object->layers().size()) - 1)->lslices); + if( bottom_area - top_area > delta_area) { + notif_mngr->push_notification( + NotificationType::SignDetected, NotificationManager::NotificationLevel::RegularNotification, + _u8L("NOTE:") + "\n" + _u8L("Sliced object looks like the sign") + "\n", + _u8L("Apply auto color change to print"), + [this/*, notif_mngr*/](wxEvtHandler*) { + // notif_mngr->close_notification_of_type(NotificationType::SignDetected); + m_layers_slider->auto_color_change(); + return true; + }); + + notif_mngr->set_in_preview(true); + + break; + } + } + } + m_layers_slider_sizer->Show((size_t)0); Layout(); } diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 266814e09c..eb028d3d36 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -1298,7 +1298,9 @@ void NotificationManager::set_in_preview(bool preview) m_in_preview = preview; for (std::unique_ptr ¬ification : m_pop_notifications) { if (notification->get_type() == NotificationType::PlaterWarning) - notification->hide(preview); + notification->hide(preview); + if (notification->get_type() == NotificationType::SignDetected) + notification->hide(!preview); } } diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 4b32a716ff..222d6b1552 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -74,6 +74,8 @@ enum class NotificationType CustomSupportsAndSeamRemovedAfterRepair, // Notification that auto adding of color changes is impossible EmptyAutoColorChange, + // Notification about detected sign + SignDetected, // Notification emitted by Print::validate PrintValidateWarning, // Notification telling user to quit SLA supports manual editing From 972dbe238f76aaf95dd232a6a655d9ea3a2d95b8 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Sun, 14 Mar 2021 18:01:10 +0100 Subject: [PATCH 096/285] preventing hidden notification to show when updated and close them correctly. --- src/slic3r/GUI/GLCanvas3D.cpp | 1 + src/slic3r/GUI/NotificationManager.cpp | 28 +++++++++++++++++--------- src/slic3r/GUI/NotificationManager.hpp | 27 ++++++++++++++++--------- 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 0a2b5cd655..5352e21b3e 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1752,6 +1752,7 @@ void GLCanvas3D::render() m_tooltip.render(m_mouse.position, *this); wxGetApp().plater()->get_mouse3d_controller().render_settings_dialog(*this); + wxGetApp().plater()->get_notification_manager()->render_notifications(*this, get_overlay_window_width()); wxGetApp().imgui()->render(); diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index eb028d3d36..a9ac68036a 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -1,12 +1,9 @@ #include "NotificationManager.hpp" -#include "GUI_App.hpp" + #include "GUI.hpp" -#include "Plater.hpp" -#include "GLCanvas3D.hpp" #include "ImGuiWrapper.hpp" #include "PrintHostDialogs.hpp" - #include "wxExtensions.hpp" #include @@ -147,6 +144,12 @@ void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float init return; } + if (m_state == EState::ClosePending || m_state == EState::Finished) + { + m_state = EState::Finished; + return; + } + Size cnv_size = canvas.get_canvas_size(); ImGuiWrapper& imgui = *wxGetApp().imgui(); ImVec2 mouse_pos = ImGui::GetMousePos(); @@ -240,7 +243,11 @@ void NotificationManager::PopNotification::count_spaces() void NotificationManager::PopNotification::init() { - std::string text = m_text1 + " " + m_hypertext; + // Do not init closing notification + if (is_finished()) + return; + + std::string text = m_text1 + " " + m_hypertext; size_t last_end = 0; m_lines_count = 0; @@ -291,7 +298,9 @@ void NotificationManager::PopNotification::init() } if (m_lines_count == 3) m_multiline = true; - m_state = EState::Shown; + m_notification_start = GLCanvas3D::timestamp_now(); + //if (m_state != EState::Hidden) + // m_state = EState::Shown; } void NotificationManager::PopNotification::set_next_window_size(ImGuiWrapper& imgui) { @@ -653,6 +662,7 @@ void NotificationManager::SlicingCompleteLargeNotification::set_large(bool l) // m_counting_down = !l; m_hypertext = l ? _u8L("Export G-Code.") : std::string(); m_state = l ? EState::Shown : EState::Hidden; + init(); } //---------------ExportFinishedNotification----------- void NotificationManager::ExportFinishedNotification::count_spaces() @@ -1270,9 +1280,10 @@ bool NotificationManager::activate_existing(const NotificationManager::PopNotifi for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end(); ++it) { if ((*it)->get_type() == new_type && !(*it)->is_finished()) { if (std::find(m_multiple_types.begin(), m_multiple_types.end(), new_type) != m_multiple_types.end()) { - //if (new_type == NotificationType::CustomNotification || new_type == NotificationType::PlaterWarning || new_type == NotificationType::ProgressBar) { - if (!(*it)->compare_text(new_text)) + // If found same type and same text, return true - update will be performed on the old notif + if ((*it)->compare_text(new_text) == false) { continue; + } } else if (new_type == NotificationType::SlicingWarning) { auto w1 = dynamic_cast(notification); auto w2 = dynamic_cast(it->get()); @@ -1284,7 +1295,6 @@ bool NotificationManager::activate_existing(const NotificationManager::PopNotifi continue; } } - if (it != m_pop_notifications.end() - 1) std::rotate(it, it + 1, m_pop_notifications.end()); return true; diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 222d6b1552..e86ac8056a 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -1,6 +1,9 @@ #ifndef slic3r_GUI_NotificationManager_hpp_ #define slic3r_GUI_NotificationManager_hpp_ +#include "GUI_App.hpp" +#include "Plater.hpp" +#include "GLCanvas3D.hpp" #include "Event.hpp" #include "I18N.hpp" @@ -211,9 +214,9 @@ private: PopNotification(const NotificationData &n, NotificationIDProvider &id_provider, wxEvtHandler* evt_handler); virtual ~PopNotification() { if (m_id) m_id_provider.release_id(m_id); } - void render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width); + virtual void render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width); // close will dissapear notification on next render - virtual void close() { m_state = EState::ClosePending; } + virtual void close() { m_state = EState::ClosePending; wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);} // data from newer notification of same type void update(const NotificationData& n); bool is_finished() const { return m_state == EState::ClosePending || m_state == EState::Finished; } @@ -226,7 +229,7 @@ private: const bool is_gray() const { return m_is_gray; } void set_gray(bool g) { m_is_gray = g; } bool compare_text(const std::string& text); - void hide(bool h) { m_state = h ? EState::Hidden : EState::Unknown; } + void hide(bool h) { if (is_finished()) return; m_state = h ? EState::Hidden : EState::Unknown; } // sets m_next_render with time of next mandatory rendering. Delta is time since last render. bool update_state(bool paused, const int64_t delta); int64_t next_render() const { return is_finished() ? 0 : m_next_render; } @@ -313,10 +316,16 @@ private: { public: SlicingCompleteLargeNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, bool largeds); - void set_large(bool l); - bool get_large() { return m_is_large; } - - void set_print_info(const std::string &info); + void set_large(bool l); + bool get_large() { return m_is_large; } + void set_print_info(const std::string &info); + virtual void render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width) + { + // This notification is always hidden if !large (means side bar is collapsed) + if (!get_large() && !is_finished()) + m_state == EState::Hidden; + PopNotification::render(canvas, initial_y, move_from_overlay, overlay_width); + } protected: virtual void render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, @@ -339,8 +348,8 @@ private: { public: PlaterWarningNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler) : PopNotification(n, id_provider, evt_handler) {} - virtual void close() { m_state = EState::Hidden; } - void real_close() { m_state = EState::ClosePending; } + virtual void close() { if(is_finished()) return; m_state = EState::Hidden; wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); } + void real_close() { m_state = EState::ClosePending; wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); } void show() { m_state = EState::Unknown; } }; From ffb13767f44d0d4aaf71f8edbc9d5863c15f86fa Mon Sep 17 00:00:00 2001 From: David Kocik Date: Sun, 14 Mar 2021 18:35:36 +0100 Subject: [PATCH 097/285] typo correction --- src/slic3r/GUI/NotificationManager.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index e86ac8056a..651deace84 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -323,7 +323,7 @@ private: { // This notification is always hidden if !large (means side bar is collapsed) if (!get_large() && !is_finished()) - m_state == EState::Hidden; + m_state = EState::Hidden; PopNotification::render(canvas, initial_y, move_from_overlay, overlay_width); } protected: From 1569dad5de053920328fc9888afdadcfb62df0aa Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 15 Mar 2021 09:54:24 +0100 Subject: [PATCH 098/285] Auto color change: Fixed show/hide for the notification. --- src/slic3r/GUI/GUI_Preview.cpp | 6 +++--- src/slic3r/GUI/Plater.cpp | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index da7d7810ad..9f9f20ffb6 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -645,7 +645,7 @@ void Preview::update_layers_slider(const std::vector& layers_z, bool kee if (ticks_info_from_model.gcodes.empty()) { NotificationManager* notif_mngr = wxGetApp().plater()->get_notification_manager(); -// notif_mngr->close_notification_of_type(NotificationType::SignDetected); + notif_mngr->close_notification_of_type(NotificationType::SignDetected); const Print& print = wxGetApp().plater()->fff_print(); double delta_area = scale_(scale_(25)); // equal to 25 mm2 @@ -675,8 +675,8 @@ void Preview::update_layers_slider(const std::vector& layers_z, bool kee NotificationType::SignDetected, NotificationManager::NotificationLevel::RegularNotification, _u8L("NOTE:") + "\n" + _u8L("Sliced object looks like the sign") + "\n", _u8L("Apply auto color change to print"), - [this/*, notif_mngr*/](wxEvtHandler*) { - // notif_mngr->close_notification_of_type(NotificationType::SignDetected); + [this, notif_mngr](wxEvtHandler*) { + notif_mngr->close_notification_of_type(NotificationType::SignDetected); m_layers_slider->auto_color_change(); return true; }); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 3d4d381fb8..c6b3ed89f8 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3563,6 +3563,7 @@ void Plater::priv::on_slicing_began() { clear_warnings(); notification_manager->close_notification_of_type(NotificationType::SlicingComplete); + notification_manager->close_notification_of_type(NotificationType::SignDetected); } void Plater::priv::add_warning(const Slic3r::PrintStateBase::Warning& warning, size_t oid) { From ceea9de8b8b1d4b35bdd1180051033680c405ba1 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 8 Mar 2021 13:44:00 +0100 Subject: [PATCH 099/285] WIP: Refactored bridging flow from normal flow, new config value 'thick_bridges' to switch between the Slic3r vs. S3D/Cura/Ideamaker way of printing 1st object layer over supports. Simplified the PresetHints. --- src/libslic3r/Brim.cpp | 8 +- src/libslic3r/ElephantFootCompensation.cpp | 2 +- src/libslic3r/ExtrusionEntity.cpp | 4 +- src/libslic3r/Fill/Fill.cpp | 67 ++++++---------- src/libslic3r/Flow.cpp | 68 +++++++--------- src/libslic3r/Flow.hpp | 40 ++++++---- src/libslic3r/GCode.cpp | 19 +++-- src/libslic3r/Layer.hpp | 4 +- src/libslic3r/LayerRegion.cpp | 24 +++--- src/libslic3r/PerimeterGenerator.cpp | 28 +++---- src/libslic3r/Preset.cpp | 2 +- src/libslic3r/Print.cpp | 10 +-- src/libslic3r/Print.hpp | 3 +- src/libslic3r/PrintConfig.cpp | 7 ++ src/libslic3r/PrintConfig.hpp | 2 + src/libslic3r/PrintObject.cpp | 12 +-- src/libslic3r/PrintRegion.cpp | 53 ++++++------ src/libslic3r/SupportMaterial.cpp | 62 +++++++-------- src/slic3r/GUI/PresetHints.cpp | 93 ++++++---------------- src/slic3r/GUI/Tab.cpp | 1 + tests/fff_print/test_flow.cpp | 2 +- 21 files changed, 228 insertions(+), 283 deletions(-) diff --git a/src/libslic3r/Brim.cpp b/src/libslic3r/Brim.cpp index d5ec0d9285..08bedc5c04 100644 --- a/src/libslic3r/Brim.cpp +++ b/src/libslic3r/Brim.cpp @@ -320,7 +320,7 @@ static void make_inner_brim(const Print &print, const ConstPrintObjectPtrs &top_ loops = union_pt_chained_outside_in(loops, false); std::reverse(loops.begin(), loops.end()); extrusion_entities_append_loops(brim.entities, std::move(loops), erSkirt, float(flow.mm3_per_mm()), - float(flow.width), float(print.skirt_first_layer_height())); + float(flow.width()), float(print.skirt_first_layer_height())); } // Produce brim lines around those objects, that have the brim enabled. @@ -495,7 +495,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance if (i + 1 == j && first_path.size() > 3 && first_path.front().X == first_path.back().X && first_path.front().Y == first_path.back().Y) { auto *loop = new ExtrusionLoop(); brim.entities.emplace_back(loop); - loop->paths.emplace_back(erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(print.skirt_first_layer_height())); + loop->paths.emplace_back(erSkirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height())); Points &points = loop->paths.front().polyline.points; points.reserve(first_path.size()); for (const ClipperLib_Z::IntPoint &pt : first_path) @@ -506,7 +506,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance ExtrusionEntityCollection this_loop_trimmed; this_loop_trimmed.entities.reserve(j - i); for (; i < j; ++ i) { - this_loop_trimmed.entities.emplace_back(new ExtrusionPath(erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(print.skirt_first_layer_height()))); + this_loop_trimmed.entities.emplace_back(new ExtrusionPath(erSkirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height()))); const ClipperLib_Z::Path &path = *loops_trimmed_order[i].first; Points &points = static_cast(this_loop_trimmed.entities.back())->polyline.points; points.reserve(path.size()); @@ -522,7 +522,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance } } } else { - extrusion_entities_append_loops_and_paths(brim.entities, std::move(all_loops), erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(print.skirt_first_layer_height())); + extrusion_entities_append_loops_and_paths(brim.entities, std::move(all_loops), erSkirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height())); } make_inner_brim(print, top_level_objects_with_brim, brim); diff --git a/src/libslic3r/ElephantFootCompensation.cpp b/src/libslic3r/ElephantFootCompensation.cpp index f28d88f7e9..0895e16d68 100644 --- a/src/libslic3r/ElephantFootCompensation.cpp +++ b/src/libslic3r/ElephantFootCompensation.cpp @@ -621,7 +621,7 @@ ExPolygon elephant_foot_compensation(const ExPolygon &input_expoly, double min_c ExPolygon elephant_foot_compensation(const ExPolygon &input, const Flow &external_perimeter_flow, const double compensation) { // The contour shall be wide enough to apply the external perimeter plus compensation on both sides. - double min_contour_width = double(external_perimeter_flow.width + external_perimeter_flow.spacing()); + double min_contour_width = double(external_perimeter_flow.width() + external_perimeter_flow.spacing()); return elephant_foot_compensation(input, min_contour_width, compensation); } diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp index 390d107f27..3284bc39e4 100644 --- a/src/libslic3r/ExtrusionEntity.cpp +++ b/src/libslic3r/ExtrusionEntity.cpp @@ -52,7 +52,9 @@ void ExtrusionPath::polygons_covered_by_spacing(Polygons &out, const float scale { // Instantiating the Flow class to get the line spacing. // Don't know the nozzle diameter, setting to zero. It shall not matter it shall be optimized out by the compiler. - Flow flow(this->width, this->height, 0.f, is_bridge(this->role())); + bool bridge = is_bridge(this->role()); + assert(! bridge || this->width == this->height); + auto flow = bridge ? Flow::bridging_flow(this->width, 0.f) : Flow(this->width, this->height, 0.f); polygons_append(out, offset(this->polyline, 0.5f * float(flow.scaled_spacing()) + scaled_epsilon)); } diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index ee493ca9cb..129a9440cf 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -42,7 +42,7 @@ struct SurfaceFillParams // width, height of extrusion, nozzle diameter, is bridge // For the output, for fill generator. - Flow flow = Flow(0.f, 0.f, 0.f, false); + Flow flow; // For the output ExtrusionRole extrusion_role = ExtrusionRole(0); @@ -70,10 +70,10 @@ struct SurfaceFillParams // RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_adjust); RETURN_COMPARE_NON_EQUAL(anchor_length); RETURN_COMPARE_NON_EQUAL(anchor_length_max); - RETURN_COMPARE_NON_EQUAL(flow.width); - RETURN_COMPARE_NON_EQUAL(flow.height); - RETURN_COMPARE_NON_EQUAL(flow.nozzle_diameter); - RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, flow.bridge); + RETURN_COMPARE_NON_EQUAL(flow.width()); + RETURN_COMPARE_NON_EQUAL(flow.height()); + RETURN_COMPARE_NON_EQUAL(flow.nozzle_diameter()); + RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, flow.bridge()); RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, extrusion_role); return false; } @@ -143,17 +143,12 @@ std::vector group_fills(const Layer &layer) params.bridge_angle = float(surface.bridge_angle); params.angle = float(Geometry::deg2rad(region_config.fill_angle.value)); - // calculate the actual flow we'll be using for this infill - params.flow = layerm.region()->flow( - extrusion_role, - (surface.thickness == -1) ? layer.height : surface.thickness, // extrusion height - is_bridge || Fill::use_bridge_flow(params.pattern), // bridge flow? - layer.id() == 0, // first layer? - -1, // auto width - *layer.object() - ); - - // Calculate flow spacing for infill pattern generation. + // Calculate the actual flow we'll be using for this infill. + params.flow = is_bridge || Fill::use_bridge_flow(params.pattern) ? + layerm.bridging_flow(extrusion_role) : + layerm.region()->flow(*layer.object(), extrusion_role, (surface.thickness == -1) ? layer.height : surface.thickness, layer.id() == 0); + + // Calculate flow spacing for infill pattern generation. if (surface.is_solid() || is_bridge) { params.spacing = params.flow.spacing(); // Don't limit anchor length for solid or bridging infill. @@ -164,14 +159,7 @@ std::vector group_fills(const Layer &layer) // for all layers, for avoiding the ugly effect of // misaligned infill on first layer because of different extrusion width and // layer height - params.spacing = layerm.region()->flow( - frInfill, - layer.object()->config().layer_height.value, // TODO: handle infill_every_layers? - false, // no bridge - false, // no first layer - -1, // auto width - *layer.object() - ).spacing(); + params.spacing = layerm.region()->flow(*layer.object(), frInfill, layer.object()->config().layer_height).spacing(); // Anchor a sparse infill to inner perimeters with the following anchor length: params.anchor_length = float(region_config.infill_anchor); if (region_config.infill_anchor.percent) @@ -278,7 +266,7 @@ std::vector group_fills(const Layer &layer) region_id = region_some_infill; const LayerRegion& layerm = *layer.regions()[region_id]; for (SurfaceFill &surface_fill : surface_fills) - if (surface_fill.surface.surface_type == stInternalSolid && std::abs(layer.height - surface_fill.params.flow.height) < EPSILON) { + if (surface_fill.surface.surface_type == stInternalSolid && std::abs(layer.height - surface_fill.params.flow.height()) < EPSILON) { internal_solid_fill = &surface_fill; break; } @@ -290,14 +278,7 @@ std::vector group_fills(const Layer &layer) params.extrusion_role = erInternalInfill; params.angle = float(Geometry::deg2rad(layerm.region()->config().fill_angle.value)); // calculate the actual flow we'll be using for this infill - params.flow = layerm.region()->flow( - frSolidInfill, - layer.height, // extrusion height - false, // bridge flow? - layer.id() == 0, // first layer? - -1, // auto width - *layer.object() - ); + params.flow = layerm.region()->flow(*layer.object(), frSolidInfill, layer.height, layer.id() == 0); params.spacing = params.flow.spacing(); surface_fills.emplace_back(params); surface_fills.back().surface.surface_type = stInternalSolid; @@ -365,9 +346,9 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree; // calculate flow spacing for infill pattern generation - bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.flow.bridge; + bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.flow.bridge(); double link_max_length = 0.; - if (! surface_fill.params.flow.bridge) { + if (! surface_fill.params.flow.bridge()) { #if 0 link_max_length = layerm.region()->config().get_abs_value(surface.is_external() ? "external_fill_link_max_length" : "fill_link_max_length", flow.spacing()); // printf("flow spacing: %f, is_external: %d, link_max_length: %lf\n", flow.spacing(), int(surface.is_external()), link_max_length); @@ -380,7 +361,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: // Maximum length of the perimeter segment linking two infill lines. f->link_max_length = (coord_t)scale_(link_max_length); // Used by the concentric infill pattern to clip the loops to create extrusion paths. - f->loop_clipping = coord_t(scale_(surface_fill.params.flow.nozzle_diameter) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER); + f->loop_clipping = coord_t(scale_(surface_fill.params.flow.nozzle_diameter()) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER); // apply half spacing using this flow's own spacing and generate infill FillParams params; @@ -402,15 +383,17 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: // calculate actual flow from spacing (which might have been adjusted by the infill // pattern generator) double flow_mm3_per_mm = surface_fill.params.flow.mm3_per_mm(); - double flow_width = surface_fill.params.flow.width; + double flow_width = surface_fill.params.flow.width(); if (using_internal_flow) { // if we used the internal flow we're not doing a solid infill // so we can safely ignore the slight variation that might have // been applied to f->spacing } else { - Flow new_flow = Flow::new_from_spacing(float(f->spacing), surface_fill.params.flow.nozzle_diameter, surface_fill.params.flow.height, surface_fill.params.flow.bridge); + Flow new_flow = surface_fill.params.flow.bridge() ? + Flow::bridging_flow_from_spacing(float(f->spacing), surface_fill.params.flow.nozzle_diameter()) : + Flow::new_from_spacing(float(f->spacing), surface_fill.params.flow.nozzle_diameter(), surface_fill.params.flow.height()); flow_mm3_per_mm = new_flow.mm3_per_mm(); - flow_width = new_flow.width; + flow_width = new_flow.width(); } // Save into layer. ExtrusionEntityCollection* eec = nullptr; @@ -420,7 +403,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: extrusion_entities_append_paths( eec->entities, std::move(polylines), surface_fill.params.extrusion_role, - flow_mm3_per_mm, float(flow_width), surface_fill.params.flow.height); + flow_mm3_per_mm, float(flow_width), surface_fill.params.flow.height()); } } } @@ -619,7 +602,7 @@ void Layer::make_ironing() fill.angle = float(ironing_params.angle + 0.25 * M_PI); fill.link_max_length = (coord_t)scale_(3. * fill.spacing); double height = ironing_params.height * fill.spacing / nozzle_dmr; - Flow flow = Flow::new_from_spacing(float(nozzle_dmr), 0., float(height), false); + Flow flow = Flow::new_from_spacing(float(nozzle_dmr), 0., float(height)); double flow_mm3_per_mm = flow.mm3_per_mm(); Surface surface_fill(stTop, ExPolygon()); for (ExPolygon &expoly : ironing_areas) { @@ -638,7 +621,7 @@ void Layer::make_ironing() extrusion_entities_append_paths( eec->entities, std::move(polylines), erIroning, - flow_mm3_per_mm, float(flow.width), float(height)); + flow_mm3_per_mm, float(flow.width()), float(height)); } } } diff --git a/src/libslic3r/Flow.cpp b/src/libslic3r/Flow.cpp index e5dcf07310..103123cdb2 100644 --- a/src/libslic3r/Flow.cpp +++ b/src/libslic3r/Flow.cpp @@ -6,6 +6,12 @@ #include +// Overlap factor of perimeter lines. Currently no overlap. +// #define HAS_PERIMETER_LINE_OVERLAP +#ifdef HAS_PERIMETER_LINE_OVERLAP + #define PERIMETER_LINE_OVERLAP_FACTOR 1.0 +#endif + // Mark string for localization and translate. #define L(s) Slic3r::I18N::translate(s) @@ -122,20 +128,13 @@ double Flow::extrusion_width(const std::string& opt_key, const ConfigOptionResol // This constructor builds a Flow object from an extrusion width config setting // and other context properties. -Flow Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio) +Flow Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height) { - // we need layer height unless it's a bridge - if (height <= 0 && bridge_flow_ratio == 0) + if (height <= 0) throw Slic3r::InvalidArgument("Invalid flow height supplied to new_from_config_width()"); float w; - if (bridge_flow_ratio > 0) { - // If bridge flow was requested, calculate the bridge width. - height = w = (bridge_flow_ratio == 1.) ? - // optimization to avoid sqrt() - nozzle_diameter : - sqrt(bridge_flow_ratio) * nozzle_diameter; - } else if (! width.percent && width.value == 0.) { + if (! width.percent && width.value == 0.) { // If user left option to 0, calculate a sane default width. w = auto_extrusion_width(role, nozzle_diameter); } else { @@ -143,26 +142,23 @@ Flow Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent w = float(width.get_abs_value(height)); } - return Flow(w, height, nozzle_diameter, bridge_flow_ratio > 0); + return Flow(w, height, nozzle_diameter, false); } // This constructor builds a Flow object from a given centerline spacing. -Flow Flow::new_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge) +Flow Flow::new_from_spacing(float spacing, float nozzle_diameter, float height) { - // we need layer height unless it's a bridge - if (height <= 0 && !bridge) + if (height <= 0) throw Slic3r::InvalidArgument("Invalid flow height supplied to new_from_spacing()"); // Calculate width from spacing. // For normal extrusons, extrusion width is wider than the spacing due to the rounding and squishing of the extrusions. - // For bridge extrusions, the extrusions are placed with a tiny BRIDGE_EXTRA_SPACING gaps between the threads. - float width = float(bridge ? - (spacing - BRIDGE_EXTRA_SPACING) : + float width = float( #ifdef HAS_PERIMETER_LINE_OVERLAP (spacing + PERIMETER_LINE_OVERLAP_FACTOR * height * (1. - 0.25 * PI)); #else (spacing + height * (1. - 0.25 * PI))); #endif - return Flow(width, bridge ? width : height, nozzle_diameter, bridge); + return Flow(width, height, nozzle_diameter); } // This method returns the centerline spacing between two adjacent extrusions @@ -170,13 +166,13 @@ Flow Flow::new_from_spacing(float spacing, float nozzle_diameter, float height, float Flow::spacing() const { #ifdef HAS_PERIMETER_LINE_OVERLAP - if (this->bridge) - return this->width + BRIDGE_EXTRA_SPACING; + if (m_bridge) + return m_width + BRIDGE_EXTRA_SPACING; // rectangle with semicircles at the ends - float min_flow_spacing = this->width - this->height * (1. - 0.25 * PI); - float res = this->width - PERIMETER_LINE_OVERLAP_FACTOR * (this->width - min_flow_spacing); + float min_flow_spacing = m_width - m_height * (1. - 0.25 * PI); + float res = m_width - PERIMETER_LINE_OVERLAP_FACTOR * (m_width - min_flow_spacing); #else - float res = float(this->bridge ? (this->width + BRIDGE_EXTRA_SPACING) : (this->width - this->height * (1. - 0.25 * PI))); + float res = float(m_bridge ? (m_width + BRIDGE_EXTRA_SPACING) : (m_width - m_height * (1. - 0.25 * PI))); #endif // assert(res > 0.f); if (res <= 0.f) @@ -189,10 +185,10 @@ float Flow::spacing() const // this->spacing(other) shall return the same value as other.spacing(*this) float Flow::spacing(const Flow &other) const { - assert(this->height == other.height); - assert(this->bridge == other.bridge); - float res = float(this->bridge ? - 0.5 * this->width + 0.5 * other.width + BRIDGE_EXTRA_SPACING : + assert(m_height == other.m_height); + assert(m_bridge == other.m_bridge); + float res = float(m_bridge ? + 0.5 * m_width + 0.5 * other.m_width + BRIDGE_EXTRA_SPACING : 0.5 * this->spacing() + 0.5 * other.spacing()); // assert(res > 0.f); if (res <= 0.f) @@ -203,11 +199,11 @@ float Flow::spacing(const Flow &other) const // This method returns extrusion volume per head move unit. double Flow::mm3_per_mm() const { - float res = this->bridge ? + float res = m_bridge ? // Area of a circle with dmr of this->width. - float((this->width * this->width) * 0.25 * PI) : + float((m_width * m_width) * 0.25 * PI) : // Rectangle with semicircles at the ends. ~ h (w - 0.215 h) - float(this->height * (this->width - this->height * (1. - 0.25 * PI))); + float(m_height * (m_width - m_height * (1. - 0.25 * PI))); //assert(res > 0.); if (res <= 0.) throw FlowErrorNegativeFlow(); @@ -222,9 +218,7 @@ Flow support_material_flow(const PrintObject *object, float layer_height) (object->config().support_material_extrusion_width.value > 0) ? object->config().support_material_extrusion_width : object->config().extrusion_width, // if object->config().support_material_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component. float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_extruder-1)), - (layer_height > 0.f) ? layer_height : float(object->config().layer_height.value), - // bridge_flow_ratio - 0.f); + (layer_height > 0.f) ? layer_height : float(object->config().layer_height.value)); } Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height) @@ -235,9 +229,7 @@ Flow support_material_1st_layer_flow(const PrintObject *object, float layer_heig // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. (width.value > 0) ? width : object->config().extrusion_width, float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_extruder-1)), - (layer_height > 0.f) ? layer_height : float(object->config().first_layer_height.get_abs_value(object->config().layer_height.value)), - // bridge_flow_ratio - 0.f); + (layer_height > 0.f) ? layer_height : float(object->config().first_layer_height.get_abs_value(object->config().layer_height.value))); } Flow support_material_interface_flow(const PrintObject *object, float layer_height) @@ -248,9 +240,7 @@ Flow support_material_interface_flow(const PrintObject *object, float layer_heig (object->config().support_material_extrusion_width > 0) ? object->config().support_material_extrusion_width : object->config().extrusion_width, // if object->config().support_material_interface_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component. float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_interface_extruder-1)), - (layer_height > 0.f) ? layer_height : float(object->config().layer_height.value), - // bridge_flow_ratio - 0.f); + (layer_height > 0.f) ? layer_height : float(object->config().layer_height.value)); } } diff --git a/src/libslic3r/Flow.hpp b/src/libslic3r/Flow.hpp index 9e57ce9079..101dc68809 100644 --- a/src/libslic3r/Flow.hpp +++ b/src/libslic3r/Flow.hpp @@ -13,11 +13,6 @@ class PrintObject; // Extra spacing of bridge threads, in mm. #define BRIDGE_EXTRA_SPACING 0.05 -// Overlap factor of perimeter lines. Currently no overlap. -#ifdef HAS_PERIMETER_LINE_OVERLAP - #define PERIMETER_LINE_OVERLAP_FACTOR 1.0 -#endif - enum FlowRole { frExternalPerimeter, frPerimeter, @@ -58,22 +53,22 @@ class Flow public: // Non bridging flow: Maximum width of an extrusion with semicircles at the ends. // Bridging flow: Bridge thread diameter. - float width; + float width() const { return m_width; } // Non bridging flow: Layer height. // Bridging flow: Bridge thread diameter = layer height. - float height; + float height() const { return m_height; } // Nozzle diameter. - float nozzle_diameter; + float nozzle_diameter() const { return m_nozzle_diameter; } // Is it a bridge? - bool bridge; + bool bridge() const { return m_bridge; } - Flow(float _w, float _h, float _nd, bool _bridge = false) : - width(_w), height(_h), nozzle_diameter(_nd), bridge(_bridge) {} + Flow() = default; + Flow(float w, float h, float nozzle_diameter) : Flow(w, h, nozzle_diameter, false) {} float spacing() const; float spacing(const Flow &other) const; double mm3_per_mm() const; - coord_t scaled_width() const { return coord_t(scale_(this->width)); } + coord_t scaled_width() const { return coord_t(scale_(m_width)); } coord_t scaled_spacing() const { return coord_t(scale_(this->spacing())); } coord_t scaled_spacing(const Flow &other) const { return coord_t(scale_(this->spacing(other))); } @@ -83,13 +78,20 @@ public: // Here an overlap of 0.2x external perimeter spacing is allowed for by the elephant foot compensation. coord_t scaled_elephant_foot_spacing() const { return coord_t(0.5f * float(this->scaled_width() + 0.6f * this->scaled_spacing())); } - bool operator==(const Flow &rhs) const { return this->width == rhs.width && this->height == rhs.height && this->nozzle_diameter == rhs.nozzle_diameter && this->bridge == rhs.bridge; } + bool operator==(const Flow &rhs) const { return m_width == rhs.m_width && m_height == rhs.m_height && m_nozzle_diameter == rhs.m_nozzle_diameter && m_bridge == rhs.m_bridge; } + + Flow with_width (float width) const { assert(! m_bridge); return Flow(width, m_height, m_nozzle_diameter, m_bridge); } + Flow with_height(float height) const { assert(! m_bridge); return Flow(m_width, height, m_nozzle_diameter, m_bridge); } + + static Flow bridging_flow(float dmr, float nozzle_diameter) { return Flow { dmr, dmr, nozzle_diameter, true }; } + static Flow bridging_flow_from_spacing(float spacing, float nozzle_diameter) + { auto dmr = spacing - float(BRIDGE_EXTRA_SPACING); return Flow { dmr, dmr, nozzle_diameter, true }; } - static Flow new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio); + static Flow new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height); // Create a flow from the spacing of extrusion lines. // This method is used exclusively to calculate new flow of 100% infill, where the extrusion width was allowed to scale // to fit a region with integer number of lines. - static Flow new_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge); + static Flow new_from_spacing(float spacing, float nozzle_diameter, float height); // Sane extrusion width defautl based on nozzle diameter. // The defaults were derived from manual Prusa MK3 profiles. @@ -100,6 +102,14 @@ public: // on active extruder etc. Therefore the value calculated by this function shall be used as a hint only. static double extrusion_width(const std::string &opt_key, const ConfigOptionFloatOrPercent *opt, const ConfigOptionResolver &config, const unsigned int first_printing_extruder = 0); static double extrusion_width(const std::string &opt_key, const ConfigOptionResolver &config, const unsigned int first_printing_extruder = 0); + +private: + Flow(float w, float h, float nozzle_diameter, bool bridge) : m_width(w), m_height(h), m_nozzle_diameter(nozzle_diameter), m_bridge(bridge) {} + + float m_width { 0 }; + float m_height { 0 }; + float m_nozzle_diameter { 0 }; + bool m_bridge { false }; }; extern Flow support_material_flow(const PrintObject *object, float layer_height = 0.f); diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 5120357985..fd6c82b84f 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1105,15 +1105,15 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu const double layer_height = first_object->config().layer_height.value; const double first_layer_height = first_object->config().first_layer_height.get_abs_value(layer_height); for (const PrintRegion* region : print.regions()) { - _write_format(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(frExternalPerimeter, layer_height, false, false, -1., *first_object).width); - _write_format(file, "; perimeters extrusion width = %.2fmm\n", region->flow(frPerimeter, layer_height, false, false, -1., *first_object).width); - _write_format(file, "; infill extrusion width = %.2fmm\n", region->flow(frInfill, layer_height, false, false, -1., *first_object).width); - _write_format(file, "; solid infill extrusion width = %.2fmm\n", region->flow(frSolidInfill, layer_height, false, false, -1., *first_object).width); - _write_format(file, "; top infill extrusion width = %.2fmm\n", region->flow(frTopSolidInfill, layer_height, false, false, -1., *first_object).width); + _write_format(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(*first_object, frExternalPerimeter, layer_height).width()); + _write_format(file, "; perimeters extrusion width = %.2fmm\n", region->flow(*first_object, frPerimeter, layer_height).width()); + _write_format(file, "; infill extrusion width = %.2fmm\n", region->flow(*first_object, frInfill, layer_height).width()); + _write_format(file, "; solid infill extrusion width = %.2fmm\n", region->flow(*first_object, frSolidInfill, layer_height).width()); + _write_format(file, "; top infill extrusion width = %.2fmm\n", region->flow(*first_object, frTopSolidInfill, layer_height).width()); if (print.has_support_material()) - _write_format(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width); + _write_format(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width()); if (print.config().first_layer_extrusion_width.value > 0) - _write_format(file, "; first layer extrusion width = %.2fmm\n", region->flow(frPerimeter, first_layer_height, false, true, -1., *first_object).width); + _write_format(file, "; first layer extrusion width = %.2fmm\n", region->flow(*first_object, frPerimeter, first_layer_height, true).width()); _write_format(file, "\n"); } print.throw_if_canceled(); @@ -2170,14 +2170,13 @@ void GCode::process_layer( const std::pair loops = loops_it->second; this->set_origin(0., 0.); m_avoid_crossing_perimeters.use_external_mp(); - Flow layer_skirt_flow(print.skirt_flow()); - layer_skirt_flow.height = float(m_skirt_done.back() - (m_skirt_done.size() == 1 ? 0. : m_skirt_done[m_skirt_done.size() - 2])); + Flow layer_skirt_flow = print.skirt_flow().with_height(float(m_skirt_done.back() - (m_skirt_done.size() == 1 ? 0. : m_skirt_done[m_skirt_done.size() - 2]))); double mm3_per_mm = layer_skirt_flow.mm3_per_mm(); for (size_t i = loops.first; i < loops.second; ++i) { // Adjust flow according to this layer's layer height. ExtrusionLoop loop = *dynamic_cast(print.skirt().entities[i]); for (ExtrusionPath &path : loop.paths) { - path.height = layer_skirt_flow.height; + path.height = layer_skirt_flow.height(); path.mm3_per_mm = mm3_per_mm; } //FIXME using the support_material_speed of the 1st object printed. diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 9a3fe368d3..ef3124dcb8 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -59,7 +59,9 @@ public: // (this collection contains only ExtrusionEntityCollection objects) ExtrusionEntityCollection fills; - Flow flow(FlowRole role, bool bridge = false, double width = -1) const; + Flow flow(FlowRole role) const; + Flow bridging_flow(FlowRole role) const; + void slices_to_fill_surfaces_clipped(); void prepare_fill_surfaces(); void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces); diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 1a0bd341c4..4b8fcaef90 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -15,16 +15,14 @@ namespace Slic3r { -Flow LayerRegion::flow(FlowRole role, bool bridge, double width) const +Flow LayerRegion::flow(FlowRole role) const { - return m_region->flow( - role, - m_layer->height, - bridge, - m_layer->id() == 0, - width, - *m_layer->object() - ); + return m_region->flow(*m_layer->object(), role, m_layer->height, m_layer->id() == 0); +} + +Flow LayerRegion::bridging_flow(FlowRole role) const +{ + return this->layer()->object()->config().thick_bridges ? m_region->bridging_flow(role) : this->flow(role); } // Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces. @@ -84,7 +82,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec g.layer_id = (int)this->layer()->id(); g.ext_perimeter_flow = this->flow(frExternalPerimeter); - g.overhang_flow = this->region()->flow(frPerimeter, -1, true, false, -1, *this->layer()->object()); + g.overhang_flow = this->bridging_flow(frPerimeter); g.solid_infill_flow = this->flow(frSolidInfill); g.process(); @@ -266,11 +264,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly // would get merged into a single one while they need different directions // also, supply the original expolygon instead of the grown one, because in case // of very thin (but still working) anchors, the grown expolygon would go beyond them - BridgeDetector bd( - initial, - lower_layer->lslices, - this->flow(frInfill, true).scaled_width() - ); + BridgeDetector bd(initial, lower_layer->lslices, this->bridging_flow(frInfill).scaled_width()); #ifdef SLIC3R_DEBUG printf("Processing bridge at layer %zu:\n", this->layer()->id()); #endif diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 7cfdc58473..d33e1cfd47 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -8,7 +8,7 @@ namespace Slic3r { -static ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thick_polyline, ExtrusionRole role, Flow &flow, const float tolerance) +static ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thick_polyline, ExtrusionRole role, const Flow &flow, const float tolerance) { ExtrusionPaths paths; ExtrusionPath path(role); @@ -62,15 +62,15 @@ static ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thi path.polyline.append(line.b); // Convert from spacing to extrusion width based on the extrusion model // of a square extrusion ended with semi circles. - flow.width = unscale(w) + flow.height * float(1. - 0.25 * PI); + Flow new_flow = flow.with_width(unscale(w) + flow.height() * float(1. - 0.25 * PI)); #ifdef SLIC3R_DEBUG printf(" filling %f gap\n", flow.width); #endif - path.mm3_per_mm = flow.mm3_per_mm(); - path.width = flow.width; - path.height = flow.height; + path.mm3_per_mm = new_flow.mm3_per_mm(); + path.width = new_flow.width(); + path.height = new_flow.height(); } else { - thickness_delta = fabs(scale_(flow.width) - w); + thickness_delta = fabs(scale_(flow.width()) - w); if (thickness_delta <= tolerance) { // the width difference between this line and the current flow width is // within the accepted tolerance @@ -88,7 +88,7 @@ static ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thi return paths; } -static void variable_width(const ThickPolylines& polylines, ExtrusionRole role, Flow flow, std::vector &out) +static void variable_width(const ThickPolylines& polylines, ExtrusionRole role, const Flow &flow, std::vector &out) { // This value determines granularity of adaptive width, as G-code does not allow // variable extrusion within a single move; this value shall only affect the amount @@ -205,8 +205,8 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime paths, intersection_pl({ polygon }, perimeter_generator.lower_slices_polygons()), role, - is_external ? perimeter_generator.ext_mm3_per_mm() : perimeter_generator.mm3_per_mm(), - is_external ? perimeter_generator.ext_perimeter_flow.width : perimeter_generator.perimeter_flow.width, + is_external ? perimeter_generator.ext_mm3_per_mm() : perimeter_generator.mm3_per_mm(), + is_external ? perimeter_generator.ext_perimeter_flow.width() : perimeter_generator.perimeter_flow.width(), (float)perimeter_generator.layer_height); // get overhang paths by checking what parts of this loop fall @@ -217,8 +217,8 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime diff_pl({ polygon }, perimeter_generator.lower_slices_polygons()), erOverhangPerimeter, perimeter_generator.mm3_per_mm_overhang(), - perimeter_generator.overhang_flow.width, - perimeter_generator.overhang_flow.height); + perimeter_generator.overhang_flow.width(), + perimeter_generator.overhang_flow.height()); // Reapply the nearest point search for starting point. // We allow polyline reversal because Clipper may have randomly reversed polylines during clipping. @@ -226,8 +226,8 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime } else { ExtrusionPath path(role); path.polyline = polygon.split_at_first_point(); - path.mm3_per_mm = is_external ? perimeter_generator.ext_mm3_per_mm() : perimeter_generator.mm3_per_mm(); - path.width = is_external ? perimeter_generator.ext_perimeter_flow.width : perimeter_generator.perimeter_flow.width; + path.mm3_per_mm = is_external ? perimeter_generator.ext_mm3_per_mm() : perimeter_generator.mm3_per_mm(); + path.width = is_external ? perimeter_generator.ext_perimeter_flow.width() : perimeter_generator.perimeter_flow.width(); path.height = (float)perimeter_generator.layer_height; paths.push_back(path); } @@ -346,7 +346,7 @@ void PerimeterGenerator::process() if (this->config->thin_walls) { // the following offset2 ensures almost nothing in @thin_walls is narrower than $min_width // (actually, something larger than that still may exist due to mitering or other causes) - coord_t min_width = coord_t(scale_(this->ext_perimeter_flow.nozzle_diameter / 3)); + coord_t min_width = coord_t(scale_(this->ext_perimeter_flow.nozzle_diameter() / 3)); ExPolygons expp = offset2_ex( // medial axis requires non-overlapping geometry diff_ex(to_polygons(last), diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 67b1ebd4fb..81a14eaec9 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -430,7 +430,7 @@ const std::vector& Preset::print_options() "support_material_pattern", "support_material_with_sheath", "support_material_spacing", "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", "support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance", - "support_material_buildplate_only", "dont_support_bridges", "notes", "complete_objects", "extruder_clearance_radius", + "support_material_buildplate_only", "dont_support_bridges", "thick_bridges", "notes", "complete_objects", "extruder_clearance_radius", "extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "perimeter_extruder", "infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder", "ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index c5babb248b..79c4e72a7d 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1577,9 +1577,7 @@ Flow Print::brim_flow() const frPerimeter, width, (float)m_config.nozzle_diameter.get_at(m_regions.front()->config().perimeter_extruder-1), - (float)this->skirt_first_layer_height(), - 0 - ); + (float)this->skirt_first_layer_height()); } Flow Print::skirt_flow() const @@ -1599,9 +1597,7 @@ Flow Print::skirt_flow() const frPerimeter, width, (float)m_config.nozzle_diameter.get_at(m_objects.front()->config().support_material_extruder-1), - (float)this->skirt_first_layer_height(), - 0 - ); + (float)this->skirt_first_layer_height()); } bool Print::has_support_material() const @@ -1818,7 +1814,7 @@ void Print::_make_skirt() ExtrusionPath( erSkirt, (float)mm3_per_mm, // this will be overridden at G-code export time - flow.width, + flow.width(), (float)first_layer_height // this will be overridden at G-code export time ))); eloop.paths.back().polyline = loop.split_at_first_point(); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 7311bdc795..04cf58fa93 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -65,7 +65,8 @@ public: const PrintRegionConfig& config() const { return m_config; } // 1-based extruder identifier for this region and role. unsigned int extruder(FlowRole role) const; - Flow flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const; + Flow flow(const PrintObject &object, FlowRole role, double layer_height, bool first_layer = false) const; + Flow bridging_flow(FlowRole role) const; // Average diameter of nozzles participating on extruding this region. coordf_t nozzle_dmr_avg(const PrintConfig &print_config) const; // Average diameter of nozzles participating on extruding this region. diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index e534f140a5..b87042b897 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2415,6 +2415,13 @@ void PrintConfigDef::init_fff_params() def->max = max_temp; def->set_default_value(new ConfigOptionInts { 200 }); + def = this->add("thick_bridges", coBool); + def->label = L("Thick bridges"); + def->category = L("Layers and Perimeters"); + def->tooltip = L("Print bridges with round extrusions."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(true)); + def = this->add("thin_walls", coBool); def->label = L("Detect thin walls"); def->category = L("Layers and Perimeters"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 76085c9417..c92abf51f7 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -522,6 +522,7 @@ public: ConfigOptionInt support_material_threshold; ConfigOptionBool support_material_with_sheath; ConfigOptionFloatOrPercent support_material_xy_spacing; + ConfigOptionBool thick_bridges; ConfigOptionFloat xy_size_compensation; ConfigOptionBool wipe_into_objects; @@ -569,6 +570,7 @@ protected: OPT_PTR(support_material_xy_spacing); OPT_PTR(support_material_threshold); OPT_PTR(support_material_with_sheath); + OPT_PTR(thick_bridges); OPT_PTR(xy_size_compensation); OPT_PTR(wipe_into_objects); } diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 52bdc87e7d..5939555765 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -554,6 +554,7 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "perimeter_extrusion_width" || opt_key == "infill_overlap" || opt_key == "thin_walls" + || opt_key == "thick_bridges" || opt_key == "external_perimeters_first") { steps.emplace_back(posPerimeters); } else if ( @@ -1459,14 +1460,7 @@ void PrintObject::bridge_over_infill() if (region.config().fill_density.value == 100) continue; // get bridge flow - Flow bridge_flow = region.flow( - frSolidInfill, - -1, // layer height, not relevant for bridge flow - true, // bridge - false, // first layer - -1, // custom width, not relevant for bridge flow - *this - ); + Flow bridge_flow = region.bridging_flow(frSolidInfill); for (LayerPtrs::iterator layer_it = m_layers.begin(); layer_it != m_layers.end(); ++ layer_it) { // skip first layer @@ -1488,7 +1482,7 @@ void PrintObject::bridge_over_infill() Polygons to_bridge_pp = internal_solid; // iterate through lower layers spanned by bridge_flow - double bottom_z = layer->print_z - bridge_flow.height; + double bottom_z = layer->print_z - bridge_flow.height(); for (int i = int(layer_it - m_layers.begin()) - 1; i >= 0; --i) { const Layer* lower_layer = m_layers[i]; diff --git a/src/libslic3r/PrintRegion.cpp b/src/libslic3r/PrintRegion.cpp index 79eb647f66..acb4298212 100644 --- a/src/libslic3r/PrintRegion.cpp +++ b/src/libslic3r/PrintRegion.cpp @@ -18,31 +18,25 @@ unsigned int PrintRegion::extruder(FlowRole role) const return extruder; } -Flow PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const +Flow PrintRegion::flow(const PrintObject &object, FlowRole role, double layer_height, bool first_layer) const { ConfigOptionFloatOrPercent config_width; - if (width != -1) { - // use the supplied custom width, if any - config_width.value = width; - config_width.percent = false; + // Get extrusion width from configuration. + // (might be an absolute value, or a percent value, or zero for auto) + if (first_layer && m_print->config().first_layer_extrusion_width.value > 0) { + config_width = m_print->config().first_layer_extrusion_width; + } else if (role == frExternalPerimeter) { + config_width = m_config.external_perimeter_extrusion_width; + } else if (role == frPerimeter) { + config_width = m_config.perimeter_extrusion_width; + } else if (role == frInfill) { + config_width = m_config.infill_extrusion_width; + } else if (role == frSolidInfill) { + config_width = m_config.solid_infill_extrusion_width; + } else if (role == frTopSolidInfill) { + config_width = m_config.top_infill_extrusion_width; } else { - // otherwise, get extrusion width from configuration - // (might be an absolute value, or a percent value, or zero for auto) - if (first_layer && m_print->config().first_layer_extrusion_width.value > 0) { - config_width = m_print->config().first_layer_extrusion_width; - } else if (role == frExternalPerimeter) { - config_width = m_config.external_perimeter_extrusion_width; - } else if (role == frPerimeter) { - config_width = m_config.perimeter_extrusion_width; - } else if (role == frInfill) { - config_width = m_config.infill_extrusion_width; - } else if (role == frSolidInfill) { - config_width = m_config.solid_infill_extrusion_width; - } else if (role == frTopSolidInfill) { - config_width = m_config.top_infill_extrusion_width; - } else { - throw Slic3r::InvalidArgument("Unknown role"); - } + throw Slic3r::InvalidArgument("Unknown role"); } if (config_width.value == 0) @@ -50,8 +44,19 @@ Flow PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool fir // Get the configured nozzle_diameter for the extruder associated to the flow role requested. // Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will follback to zero'th element, so everything is all right. - double nozzle_diameter = m_print->config().nozzle_diameter.get_at(this->extruder(role) - 1); - return Flow::new_from_config_width(role, config_width, (float)nozzle_diameter, (float)layer_height, bridge ? (float)m_config.bridge_flow_ratio : 0.0f); + auto nozzle_diameter = float(m_print->config().nozzle_diameter.get_at(this->extruder(role) - 1)); + return Flow::new_from_config_width(role, config_width, nozzle_diameter, float(layer_height)); +} + +Flow PrintRegion::bridging_flow(FlowRole role) const +{ + // Get the configured nozzle_diameter for the extruder associated to the flow role requested. + // Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will follback to zero'th element, so everything is all right. + auto nozzle_diameter = float(m_print->config().nozzle_diameter.get_at(this->extruder(role) - 1)); + double bfr = m_config.bridge_flow_ratio; + if (bfr != 1.) + bfr = sqrt(bfr); + return Flow::bridging_flow(float(bfr) * nozzle_diameter, nozzle_diameter); } coordf_t PrintRegion::nozzle_dmr_avg(const PrintConfig &print_config) const diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 459a156034..7c018c9747 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -345,7 +345,7 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) if (! object->region_volumes[region_id].empty()) external_perimeter_width = std::max(external_perimeter_width, - (coordf_t)object->print()->get_region(region_id)->flow(frExternalPerimeter, slicing_params.layer_height, false, false, -1, *object).width); + (coordf_t)object->print()->get_region(region_id)->flow(*object, frExternalPerimeter, slicing_params.layer_height).width()); m_gap_xy = m_object_config->support_material_xy_spacing.get_abs_value(external_perimeter_width); m_can_merge_support_regions = m_object_config->support_material_extruder.value == m_object_config->support_material_interface_extruder.value; @@ -1231,7 +1231,7 @@ namespace SupportMaterialInternal { // since we're dealing with bridges, we can't assume width is larger than spacing, // so we take the largest value and also apply safety offset to be ensure no gaps // are left in between - Flow bridge_flow = layerm->flow(frPerimeter, true); + Flow bridge_flow = layerm->bridging_flow(frPerimeter); float w = float(std::max(bridge_flow.scaled_width(), bridge_flow.scaled_spacing())); for (Polyline &polyline : overhang_perimeters) if (polyline.is_straight()) { @@ -1542,16 +1542,20 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ } } // Offset the contact polygons outside. +#if 0 for (size_t i = 0; i < NUM_MARGIN_STEPS; ++ i) { diff_polygons = diff( offset( diff_polygons, - SUPPORT_MATERIAL_MARGIN / NUM_MARGIN_STEPS, + scaled(SUPPORT_MATERIAL_MARGIN / NUM_MARGIN_STEPS), ClipperLib::jtRound, // round mitter limit scale_(0.05)), slices_margin_cached); } +#else + diff_polygons = diff(diff_polygons, slices_margin_cached); +#endif } polygons_append(contact_polygons, diff_polygons); } // for each layer.region @@ -1597,7 +1601,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ // Contact layer will be printed with a normal flow, but // it will support layers printed with a bridging flow. - if (SupportMaterialInternal::has_bridging_extrusions(layer)) { + if (m_object_config->thick_bridges && SupportMaterialInternal::has_bridging_extrusions(layer)) { coordf_t bridging_height = 0.; for (const LayerRegion *region : layer.regions()) bridging_height += region->region()->bridging_height_avg(*m_print_config); @@ -1879,12 +1883,14 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta //FIXME Check whether the bottom bridging surfaces are extruded correctly (no bridging flow correction applied?) // According to Jindrich the bottom surfaces work well. //FIXME test the bridging flow instead? - m_support_material_interface_flow.nozzle_diameter; + m_object_config->thick_bridges.value ? m_support_material_interface_flow.nozzle_diameter() : + // Take the default layer height. + m_object_config->layer_height; layer_new.print_z = m_slicing_params.soluble_interface ? object.layers()[layer_id + 1]->print_z : layer.print_z + layer_new.height + m_object_config->support_material_contact_distance.value; layer_new.bottom_z = layer.print_z; layer_new.idx_object_layer_below = layer_id; - layer_new.bridging = ! m_slicing_params.soluble_interface; + layer_new.bridging = ! m_slicing_params.soluble_interface && m_object_config->thick_bridges; //FIXME how much to inflate the bottom surface, as it is being extruded with a bridging flow? The following line uses a normal flow. //FIXME why is the offset positive? It will be trimmed by the object later on anyway, but then it just wastes CPU clocks. layer_new.polygons = offset(touching, float(m_support_material_flow.scaled_width()), SUPPORT_SURFACES_OFFSET_PARAMETERS); @@ -2512,7 +2518,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object( break; polygons_append(polygons_trimming, offset(object_layer.lslices, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS)); } - if (! m_slicing_params.soluble_interface) { + if (! m_slicing_params.soluble_interface && m_object_config->thick_bridges) { // Collect all bottom surfaces, which will be extruded with a bridging flow. for (; i < object.layers().size(); ++ i) { const Layer &object_layer = *object.layers()[i]; @@ -2810,7 +2816,7 @@ std::pairentities, polylines, erSupportMaterial, flow.mm3_per_mm(), flow.width, flow.height); + extrusion_entities_append_paths(eec->entities, polylines, erSupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height()); // Fill in the rest. fill_expolygons_generate_paths(eec->entities, offset_ex(expoly, float(-0.4 * spacing)), filler, fill_params, density, role, flow); dst.emplace_back(eec.release()); @@ -3011,8 +3017,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const if (n_contact_loops == 0 || top_contact_layer.empty()) return; - Flow flow = interface_flow_src; - flow.height = float(top_contact_layer.layer->height); + Flow flow = interface_flow_src.with_height(top_contact_layer.layer->height); Polygons overhang_polygons; if (top_contact_layer.layer->overhang_polygons != nullptr) @@ -3209,7 +3214,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const extrusion_entities_append_paths( top_contact_layer.extrusions, std::move(loop_lines), - erSupportMaterialInterface, flow.mm3_per_mm(), flow.width, flow.height); + erSupportMaterialInterface, flow.mm3_per_mm(), flow.width(), flow.height()); } #ifdef SLIC3R_DEBUG @@ -3342,7 +3347,7 @@ void modulate_extrusion_by_overlapping_layers( // Adjust the extrusion parameters for a reduced layer height and a non-bridging flow (nozzle_dmr = -1, does not matter). assert(this_layer.print_z > overlapping_layer.print_z); frag.height = float(this_layer.print_z - overlapping_layer.print_z); - frag.mm3_per_mm = Flow(frag.width, frag.height, -1.f, false).mm3_per_mm(); + frag.mm3_per_mm = Flow(frag.width, frag.height, -1.f).mm3_per_mm(); #ifdef SLIC3R_DEBUG svg.draw(frag.polylines, dbg_index_to_color(i_overlapping_layer), scale_(0.1)); #endif /* SLIC3R_DEBUG */ @@ -3565,7 +3570,8 @@ void PrintObjectSupportMaterial::generate_toolpaths( //FIXME misusing contact_polygons for support columns. ((raft_layer.contact_polygons == nullptr) ? Polygons() : *raft_layer.contact_polygons); if (! to_infill_polygons.empty()) { - Flow flow(float(m_support_material_flow.width), float(raft_layer.height), m_support_material_flow.nozzle_diameter, raft_layer.bridging); + assert(! raft_layer.bridging); + Flow flow(float(m_support_material_flow.width()), float(raft_layer.height), m_support_material_flow.nozzle_diameter()); Fill * filler = filler_support.get(); filler->angle = raft_angle_base; filler->spacing = m_support_material_flow.spacing(); @@ -3596,7 +3602,8 @@ void PrintObjectSupportMaterial::generate_toolpaths( // We don't use $base_flow->spacing because we need a constant spacing // value that guarantees that all layers are correctly aligned. filler->spacing = m_support_material_flow.spacing(); - flow = Flow(float(m_support_material_interface_flow.width), float(raft_layer.height), m_support_material_flow.nozzle_diameter, raft_layer.bridging); + assert(! raft_layer.bridging); + flow = Flow(float(m_support_material_interface_flow.width()), float(raft_layer.height), m_support_material_flow.nozzle_diameter()); density = float(interface_density); } else continue; @@ -3734,11 +3741,10 @@ void PrintObjectSupportMaterial::generate_toolpaths( bool interface_as_base = (&layer_ex == &interface_layer) && m_object_config->support_material_interface_layers.value == 0; //FIXME Bottom interfaces are extruded with the briding flow. Some bridging layers have its height slightly reduced, therefore // the bridging flow does not quite apply. Reduce the flow to area of an ellipse? (A = pi * a * b) - Flow interface_flow( - float(layer_ex.layer->bridging ? layer_ex.layer->height : (interface_as_base ? m_support_material_flow.width : m_support_material_interface_flow.width)), - float(layer_ex.layer->height), - m_support_material_interface_flow.nozzle_diameter, - layer_ex.layer->bridging); + auto interface_flow = layer_ex.layer->bridging ? + Flow::bridging_flow(layer_ex.layer->height, m_support_material_interface_flow.nozzle_diameter()) : + Flow(interface_as_base ? m_support_material_flow.width() : m_support_material_interface_flow.width(), + float(layer_ex.layer->height), m_support_material_interface_flow.nozzle_diameter()); filler_interface->angle = interface_as_base ? // If zero interface layers are configured, use the same angle as for the base layers. angles[support_layer_id % angles.size()] : @@ -3762,11 +3768,8 @@ void PrintObjectSupportMaterial::generate_toolpaths( Fill *filler = filler_base_interface.get(); //FIXME Bottom interfaces are extruded with the briding flow. Some bridging layers have its height slightly reduced, therefore // the bridging flow does not quite apply. Reduce the flow to area of an ellipse? (A = pi * a * b) - Flow interface_flow( - float(base_interface_layer.layer->bridging ? base_interface_layer.layer->height : m_support_material_flow.width), // m_support_material_interface_flow.width)), - float(base_interface_layer.layer->height), - m_support_material_flow.nozzle_diameter, - base_interface_layer.layer->bridging); + assert(! base_interface_layer.layer->bridging); + Flow interface_flow = m_support_material_flow.with_height(float(base_interface_layer.layer->height)); filler->angle = interface_angle; filler->spacing = m_support_material_interface_flow.spacing(); filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / interface_density)); @@ -3788,11 +3791,8 @@ void PrintObjectSupportMaterial::generate_toolpaths( filler->angle = angles[support_layer_id % angles.size()]; // We don't use $base_flow->spacing because we need a constant spacing // value that guarantees that all layers are correctly aligned. - Flow flow( - float(base_layer.layer->bridging ? base_layer.layer->height : m_support_material_flow.width), - float(base_layer.layer->height), - m_support_material_flow.nozzle_diameter, - base_layer.layer->bridging); + assert(! base_layer.layer->bridging); + auto flow = m_support_material_flow.with_height(float(base_layer.layer->height)); filler->spacing = m_support_material_flow.spacing(); filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_density)); float density = float(support_density); diff --git a/src/slic3r/GUI/PresetHints.cpp b/src/slic3r/GUI/PresetHints.cpp index 8621586f8e..0e5e10d45a 100644 --- a/src/slic3r/GUI/PresetHints.cpp +++ b/src/slic3r/GUI/PresetHints.cpp @@ -148,74 +148,33 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle speed_normal = first_layer_speed.get_abs_value(speed_normal); return (speed_normal > 0.) ? speed_normal : speed_max; }; + auto test_flow = + [first_layer_extrusion_width_ptr, extrusion_width, nozzle_diameter, lh, bridging, bridge_speed, bridge_flow_ratio, limit_by_first_layer_speed, max_print_speed, &max_flow, &max_flow_extrusion_type] + (FlowRole flow_role, const ConfigOptionFloatOrPercent &this_extrusion_width, double speed, const char *err_msg) { + Flow flow = bridging ? + Flow::new_from_config_width(flow_role, first_positive(first_layer_extrusion_width_ptr, this_extrusion_width, extrusion_width), nozzle_diameter, lh) : + Flow::bridging_flow(nozzle_diameter * bridge_flow_ratio, nozzle_diameter); + double volumetric_flow = flow.mm3_per_mm() * (bridging ? bridge_speed : limit_by_first_layer_speed(speed, max_print_speed)); + if (max_flow < volumetric_flow) { + max_flow = volumetric_flow; + max_flow_extrusion_type = _utf8(err_msg); + } + }; if (perimeter_extruder_active) { - double external_perimeter_rate = Flow::new_from_config_width(frExternalPerimeter, - first_positive(first_layer_extrusion_width_ptr, external_perimeter_extrusion_width, extrusion_width), - nozzle_diameter, lh, bfr).mm3_per_mm() * - (bridging ? bridge_speed : - limit_by_first_layer_speed(std::max(external_perimeter_speed, small_perimeter_speed), max_print_speed)); - if (max_flow < external_perimeter_rate) { - max_flow = external_perimeter_rate; - max_flow_extrusion_type = _utf8(L("external perimeters")); - } - double perimeter_rate = Flow::new_from_config_width(frPerimeter, - first_positive(first_layer_extrusion_width_ptr, perimeter_extrusion_width, extrusion_width), - nozzle_diameter, lh, bfr).mm3_per_mm() * - (bridging ? bridge_speed : - limit_by_first_layer_speed(std::max(perimeter_speed, small_perimeter_speed), max_print_speed)); - if (max_flow < perimeter_rate) { - max_flow = perimeter_rate; - max_flow_extrusion_type = _utf8(L("perimeters")); - } - } - if (! bridging && infill_extruder_active) { - double infill_rate = Flow::new_from_config_width(frInfill, - first_positive(first_layer_extrusion_width_ptr, infill_extrusion_width, extrusion_width), - nozzle_diameter, lh, bfr).mm3_per_mm() * limit_by_first_layer_speed(infill_speed, max_print_speed); - if (max_flow < infill_rate) { - max_flow = infill_rate; - max_flow_extrusion_type = _utf8(L("infill")); - } + test_flow(frExternalPerimeter, external_perimeter_extrusion_width, std::max(external_perimeter_speed, small_perimeter_speed), L("external perimeters")); + test_flow(frPerimeter, perimeter_extrusion_width, std::max(perimeter_speed, small_perimeter_speed), L("perimeters")); } + if (! bridging && infill_extruder_active) + test_flow(frInfill, infill_extrusion_width, infill_speed, L("infill")); if (solid_infill_extruder_active) { - double solid_infill_rate = Flow::new_from_config_width(frInfill, - first_positive(first_layer_extrusion_width_ptr, solid_infill_extrusion_width, extrusion_width), - nozzle_diameter, lh, 0).mm3_per_mm() * - (bridging ? bridge_speed : limit_by_first_layer_speed(solid_infill_speed, max_print_speed)); - if (max_flow < solid_infill_rate) { - max_flow = solid_infill_rate; - max_flow_extrusion_type = _utf8(L("solid infill")); - } - if (! bridging) { - double top_solid_infill_rate = Flow::new_from_config_width(frInfill, - first_positive(first_layer_extrusion_width_ptr, top_infill_extrusion_width, extrusion_width), - nozzle_diameter, lh, bfr).mm3_per_mm() * limit_by_first_layer_speed(top_solid_infill_speed, max_print_speed); - if (max_flow < top_solid_infill_rate) { - max_flow = top_solid_infill_rate; - max_flow_extrusion_type = _utf8(L("top solid infill")); - } - } - } - if (support_material_extruder_active) { - double support_material_rate = Flow::new_from_config_width(frSupportMaterial, - first_positive(first_layer_extrusion_width_ptr, support_material_extrusion_width, extrusion_width), - nozzle_diameter, lh, bfr).mm3_per_mm() * - (bridging ? bridge_speed : limit_by_first_layer_speed(support_material_speed, max_print_speed)); - if (max_flow < support_material_rate) { - max_flow = support_material_rate; - max_flow_extrusion_type = _utf8(L("support")); - } - } - if (support_material_interface_extruder_active) { - double support_material_interface_rate = Flow::new_from_config_width(frSupportMaterialInterface, - first_positive(first_layer_extrusion_width_ptr, support_material_extrusion_width, extrusion_width), - nozzle_diameter, lh, bfr).mm3_per_mm() * - (bridging ? bridge_speed : limit_by_first_layer_speed(support_material_interface_speed, max_print_speed)); - if (max_flow < support_material_interface_rate) { - max_flow = support_material_interface_rate; - max_flow_extrusion_type = _utf8(L("support interface")); - } + test_flow(frInfill, solid_infill_extrusion_width, solid_infill_speed, L("solid infill")); + if (! bridging) + test_flow(frInfill, top_infill_extrusion_width, top_solid_infill_speed, L("top solid infill")); } + if (! bridging && support_material_extruder_active) + test_flow(frSupportMaterial, support_material_extrusion_width, support_material_speed, L("support")); + if (support_material_interface_extruder_active) + test_flow(frSupportMaterialInterface, support_material_extrusion_width, support_material_interface_speed, L("support interface")); //FIXME handle gap_fill_speed if (! out.empty()) out += "\n"; @@ -254,11 +213,11 @@ std::string PresetHints::recommended_thin_wall_thickness(const PresetBundle &pre Flow external_perimeter_flow = Flow::new_from_config_width( frExternalPerimeter, *print_config.opt("external_perimeter_extrusion_width"), - nozzle_diameter, layer_height, false); + nozzle_diameter, layer_height); Flow perimeter_flow = Flow::new_from_config_width( frPerimeter, *print_config.opt("perimeter_extrusion_width"), - nozzle_diameter, layer_height, false); + nozzle_diameter, layer_height); if (num_perimeters > 0) { @@ -266,7 +225,7 @@ std::string PresetHints::recommended_thin_wall_thickness(const PresetBundle &pre out += (boost::format(_utf8(L("Recommended object thin wall thickness for layer height %.2f and"))) % layer_height).str() + " "; // Start with the width of two closely spaced try { - double width = external_perimeter_flow.width + external_perimeter_flow.spacing(); + double width = external_perimeter_flow.width() + external_perimeter_flow.spacing(); for (int i = 2; i <= num_lines; thin_walls ? ++ i : i += 2) { if (i > 2) out += ", "; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index d737bae101..645d31f4bc 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1433,6 +1433,7 @@ void TabPrint::build() optgroup->append_single_option_line("avoid_crossing_perimeters", category_path + "avoid-crossing-perimeters"); optgroup->append_single_option_line("avoid_crossing_perimeters_max_detour", category_path + "avoid_crossing_perimeters_max_detour"); optgroup->append_single_option_line("thin_walls", category_path + "detect-thin-walls"); + optgroup->append_single_option_line("thick_bridges", category_path + "thick_bridges"); optgroup->append_single_option_line("overhangs", category_path + "detect-bridging-perimeters"); optgroup = page->new_optgroup(L("Advanced")); diff --git a/tests/fff_print/test_flow.cpp b/tests/fff_print/test_flow.cpp index 969ae3c828..63b62a4d73 100644 --- a/tests/fff_print/test_flow.cpp +++ b/tests/fff_print/test_flow.cpp @@ -143,7 +143,7 @@ SCENARIO("Flow: Flow math for non-bridges", "[Flow]") { GIVEN("Input spacing of 0.414159 and a total width of 2") { double in_spacing = 0.414159; double total_width = 2.0; - auto flow = Flow::new_from_spacing(1.0, 0.4, 0.3, false); + auto flow = Flow::new_from_spacing(1.0, 0.4, 0.3); WHEN("solid_spacing() is called") { double result = flow.solid_spacing(total_width, in_spacing); THEN("Yielded spacing is greater than 0") { From 8e27e355c2bb8450459d6f031cddab968bd67a3f Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 8 Mar 2021 14:29:23 +0100 Subject: [PATCH 100/285] Fixing unit tests. --- t/support.t | 1 - tests/fff_print/test_flow.cpp | 51 ++++--------------- .../test_elephant_foot_compensation.cpp | 22 ++++---- xs/lib/Slic3r/XS.pm | 3 +- xs/xsp/Flow.xsp | 21 +++----- xs/xsp/Layer.xsp | 4 +- xs/xsp/Print.xsp | 3 -- 7 files changed, 30 insertions(+), 75 deletions(-) diff --git a/t/support.t b/t/support.t index a0cac14707..0283df22b1 100644 --- a/t/support.t +++ b/t/support.t @@ -31,7 +31,6 @@ use Slic3r::Test; role => FLOW_ROLE_SUPPORT_MATERIAL, nozzle_diameter => $print->config->nozzle_diameter->[$object_config->support_material_extruder-1] // $print->config->nozzle_diameter->[0], layer_height => $object_config->layer_height, - bridge_flow_ratio => 0, ); my $support = Slic3r::Print::SupportMaterial->new( object_config => $print->print->objects->[0]->config, diff --git a/tests/fff_print/test_flow.cpp b/tests/fff_print/test_flow.cpp index 63b62a4d73..b679a07e35 100644 --- a/tests/fff_print/test_flow.cpp +++ b/tests/fff_print/test_flow.cpp @@ -96,43 +96,41 @@ SCENARIO("Flow: Flow math for non-bridges", "[Flow]") { GIVEN("Nozzle Diameter of 0.4, a desired width of 1mm and layer height of 0.5") { ConfigOptionFloatOrPercent width(1.0, false); float nozzle_diameter = 0.4f; - float bridge_flow = 0.f; float layer_height = 0.5f; // Spacing for non-bridges is has some overlap THEN("External perimeter flow has spacing fixed to 1.125 * nozzle_diameter") { - auto flow = Flow::new_from_config_width(frExternalPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height, bridge_flow); + auto flow = Flow::new_from_config_width(frExternalPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height); REQUIRE(flow.spacing() == Approx(1.125 * nozzle_diameter - layer_height * (1.0 - PI / 4.0))); } THEN("Internal perimeter flow has spacing fixed to 1.125 * nozzle_diameter") { - auto flow = Flow::new_from_config_width(frPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height, bridge_flow); + auto flow = Flow::new_from_config_width(frPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height); REQUIRE(flow.spacing() == Approx(1.125 *nozzle_diameter - layer_height * (1.0 - PI / 4.0))); } THEN("Spacing for supplied width is 0.8927f") { - auto flow = Flow::new_from_config_width(frExternalPerimeter, width, nozzle_diameter, layer_height, bridge_flow); + auto flow = Flow::new_from_config_width(frExternalPerimeter, width, nozzle_diameter, layer_height); REQUIRE(flow.spacing() == Approx(width.value - layer_height * (1.0 - PI / 4.0))); - flow = Flow::new_from_config_width(frPerimeter, width, nozzle_diameter, layer_height, bridge_flow); + flow = Flow::new_from_config_width(frPerimeter, width, nozzle_diameter, layer_height); REQUIRE(flow.spacing() == Approx(width.value - layer_height * (1.0 - PI / 4.0))); } } /// Check the min/max GIVEN("Nozzle Diameter of 0.25") { float nozzle_diameter = 0.25f; - float bridge_flow = 0.f; float layer_height = 0.5f; WHEN("layer height is set to 0.2") { layer_height = 0.15f; THEN("Max width is set.") { - auto flow = Flow::new_from_config_width(frPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height, bridge_flow); - REQUIRE(flow.width == Approx(1.125 * nozzle_diameter)); + auto flow = Flow::new_from_config_width(frPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height); + REQUIRE(flow.width() == Approx(1.125 * nozzle_diameter)); } } WHEN("Layer height is set to 0.2") { layer_height = 0.3f; THEN("Min width is set.") { - auto flow = Flow::new_from_config_width(frPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height, bridge_flow); - REQUIRE(flow.width == Approx(1.125 * nozzle_diameter)); + auto flow = Flow::new_from_config_width(frPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height); + REQUIRE(flow.width() == Approx(1.125 * nozzle_diameter)); } } } @@ -158,41 +156,12 @@ SCENARIO("Flow: Flow math for non-bridges", "[Flow]") { /// Spacing, width calculation for bridge extrusions SCENARIO("Flow: Flow math for bridges", "[Flow]") { GIVEN("Nozzle Diameter of 0.4, a desired width of 1mm and layer height of 0.5") { - auto width = ConfigOptionFloatOrPercent(1.0, false); float nozzle_diameter = 0.4f; float bridge_flow = 1.0f; - float layer_height = 0.5f; WHEN("Flow role is frExternalPerimeter") { - auto flow = Flow::new_from_config_width(frExternalPerimeter, width, nozzle_diameter, layer_height, bridge_flow); + auto flow = Flow::bridging_flow(nozzle_diameter * sqrt(bridge_flow), nozzle_diameter); THEN("Bridge width is same as nozzle diameter") { - REQUIRE(flow.width == Approx(nozzle_diameter)); - } - THEN("Bridge spacing is same as nozzle diameter + BRIDGE_EXTRA_SPACING") { - REQUIRE(flow.spacing() == Approx(nozzle_diameter + BRIDGE_EXTRA_SPACING)); - } - } - WHEN("Flow role is frInfill") { - auto flow = Flow::new_from_config_width(frInfill, width, nozzle_diameter, layer_height, bridge_flow); - THEN("Bridge width is same as nozzle diameter") { - REQUIRE(flow.width == Approx(nozzle_diameter)); - } - THEN("Bridge spacing is same as nozzle diameter + BRIDGE_EXTRA_SPACING") { - REQUIRE(flow.spacing() == Approx(nozzle_diameter + BRIDGE_EXTRA_SPACING)); - } - } - WHEN("Flow role is frPerimeter") { - auto flow = Flow::new_from_config_width(frPerimeter, width, nozzle_diameter, layer_height, bridge_flow); - THEN("Bridge width is same as nozzle diameter") { - REQUIRE(flow.width == Approx(nozzle_diameter)); - } - THEN("Bridge spacing is same as nozzle diameter + BRIDGE_EXTRA_SPACING") { - REQUIRE(flow.spacing() == Approx(nozzle_diameter + BRIDGE_EXTRA_SPACING)); - } - } - WHEN("Flow role is frSupportMaterial") { - auto flow = Flow::new_from_config_width(frSupportMaterial, width, nozzle_diameter, layer_height, bridge_flow); - THEN("Bridge width is same as nozzle diameter") { - REQUIRE(flow.width == Approx(nozzle_diameter)); + REQUIRE(flow.width() == Approx(nozzle_diameter)); } THEN("Bridge spacing is same as nozzle diameter + BRIDGE_EXTRA_SPACING") { REQUIRE(flow.spacing() == Approx(nozzle_diameter + BRIDGE_EXTRA_SPACING)); diff --git a/tests/libslic3r/test_elephant_foot_compensation.cpp b/tests/libslic3r/test_elephant_foot_compensation.cpp index 180f678c56..4e340c37a4 100644 --- a/tests/libslic3r/test_elephant_foot_compensation.cpp +++ b/tests/libslic3r/test_elephant_foot_compensation.cpp @@ -434,7 +434,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") { ExPolygon expoly = contour_with_hole(); WHEN("Compensated") { // Elephant foot compensation shall not pinch off bits from this contour. - ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f, false), 0.2f); + ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f), 0.2f); #ifdef TESTS_EXPORT_SVGS SVG::export_expolygons(debug_out_path("elephant_foot_compensation_with_hole.svg").c_str(), { { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } }, @@ -449,7 +449,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") { GIVEN("Tiny contour") { ExPolygon expoly({ { 133382606, 94912473 }, { 134232493, 95001115 }, { 133783926, 95159440 }, { 133441897, 95180666 }, { 133408242, 95191984 }, { 133339012, 95166830 }, { 132991642, 95011087 }, { 133206549, 94908304 } }); WHEN("Compensated") { - ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f, false), 0.2f); + ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f), 0.2f); #ifdef TESTS_EXPORT_SVGS SVG::export_expolygons(debug_out_path("elephant_foot_compensation_tiny.svg").c_str(), { { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } }, @@ -464,7 +464,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") { GIVEN("Large box") { ExPolygon expoly( { {50000000, 50000000 }, { 0, 50000000 }, { 0, 0 }, { 50000000, 0 } } ); WHEN("Compensated") { - ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f, false), 0.21f); + ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f), 0.21f); #ifdef TESTS_EXPORT_SVGS SVG::export_expolygons(debug_out_path("elephant_foot_compensation_large_box.svg").c_str(), { { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } }, @@ -479,7 +479,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") { GIVEN("Thin ring (GH issue #2085)") { ExPolygon expoly = thin_ring(); WHEN("Compensated") { - ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f, false), 0.25f); + ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f), 0.25f); #ifdef TESTS_EXPORT_SVGS SVG::export_expolygons(debug_out_path("elephant_foot_compensation_thin_ring.svg").c_str(), { { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } }, @@ -532,7 +532,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") { expoly = union_ex({ expoly, expoly2 }).front(); WHEN("Partially compensated") { - ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.45f, 0.2f, 0.4f, false), 0.25f); + ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.45f, 0.2f, 0.4f), 0.25f); #ifdef TESTS_EXPORT_SVGS SVG::export_expolygons(debug_out_path("elephant_foot_compensation_0.svg").c_str(), { { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } }, @@ -543,7 +543,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") { } } WHEN("Fully compensated") { - ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.35f, 0.2f, 0.4f, false), 0.17f); + ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.35f, 0.2f, 0.4f), 0.17f); #ifdef TESTS_EXPORT_SVGS SVG::export_expolygons(debug_out_path("elephant_foot_compensation_1.svg").c_str(), { { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } }, @@ -558,7 +558,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") { GIVEN("Box with hole close to wall (GH issue #2998)") { ExPolygon expoly = box_with_hole_close_to_wall(); WHEN("Compensated") { - ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f, false), 0.25f); + ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f), 0.25f); #ifdef TESTS_EXPORT_SVGS SVG::export_expolygons(debug_out_path("elephant_foot_compensation_2.svg").c_str(), { { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } }, @@ -575,7 +575,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") { ExPolygon expoly = spirograph_gear_1mm(); WHEN("Partially compensated") { - ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.45f, 0.2f, 0.4f, false), 0.25f); + ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.45f, 0.2f, 0.4f), 0.25f); #ifdef TESTS_EXPORT_SVGS SVG::export_expolygons(debug_out_path("elephant_foot_compensation_2.svg").c_str(), { { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } }, @@ -586,7 +586,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") { } } WHEN("Fully compensated") { - ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.35f, 0.2f, 0.4f, false), 0.17f); + ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.35f, 0.2f, 0.4f), 0.17f); #ifdef TESTS_EXPORT_SVGS SVG::export_expolygons(debug_out_path("elephant_foot_compensation_3.svg").c_str(), { { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } }, @@ -597,7 +597,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") { } } WHEN("Brutally compensated") { - ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.45f, 0.2f, 0.4f, false), 0.6f); + ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.45f, 0.2f, 0.4f), 0.6f); #ifdef TESTS_EXPORT_SVGS SVG::export_expolygons(debug_out_path("elephant_foot_compensation_4.svg").c_str(), { { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } }, @@ -612,7 +612,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") { GIVEN("Vase with fins") { ExPolygon expoly = vase_with_fins(); WHEN("Compensated") { - ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f, false), 0.41f); + ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f), 0.41f); #ifdef TESTS_EXPORT_SVGS SVG::export_expolygons(debug_out_path("elephant_foot_compensation_vase_with_fins.svg").c_str(), { { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } }, diff --git a/xs/lib/Slic3r/XS.pm b/xs/lib/Slic3r/XS.pm index fa4dde43a2..6d3bf35cf2 100644 --- a/xs/lib/Slic3r/XS.pm +++ b/xs/lib/Slic3r/XS.pm @@ -158,7 +158,6 @@ sub new { my $self = $class->_new( @args{qw(width height nozzle_diameter)}, ); - $self->set_bridge($args{bridge} // 0); return $self; } @@ -166,7 +165,7 @@ sub new_from_width { my ($class, %args) = @_; return $class->_new_from_width( - @args{qw(role width nozzle_diameter layer_height bridge_flow_ratio)}, + @args{qw(role width nozzle_diameter layer_height)}, ); } diff --git a/xs/xsp/Flow.xsp b/xs/xsp/Flow.xsp index b57df5e37a..6962085d51 100644 --- a/xs/xsp/Flow.xsp +++ b/xs/xsp/Flow.xsp @@ -8,21 +8,13 @@ %name{Slic3r::Flow} class Flow { ~Flow(); %name{_new} Flow(float width, float height, float nozzle_diameter); - void set_height(float height) - %code{% THIS->height = height; %}; - void set_bridge(bool bridge) - %code{% THIS->bridge = bridge; %}; Clone clone() %code{% RETVAL = THIS; %}; - float width() - %code{% RETVAL = THIS->width; %}; - float height() - %code{% RETVAL = THIS->height; %}; - float nozzle_diameter() - %code{% RETVAL = THIS->nozzle_diameter; %}; - bool bridge() - %code{% RETVAL = THIS->bridge; %}; + float width(); + float height(); + float nozzle_diameter(); + bool bridge(); float spacing(); float spacing_to(Flow* other) %code{% RETVAL = THIS->spacing(*other); %}; @@ -32,17 +24,16 @@ %{ Flow* -_new_from_width(CLASS, role, width, nozzle_diameter, height, bridge_flow_ratio) +_new_from_width(CLASS, role, width, nozzle_diameter, height) char* CLASS; FlowRole role; std::string width; float nozzle_diameter; float height; - float bridge_flow_ratio; CODE: ConfigOptionFloatOrPercent optwidth; optwidth.deserialize(width); - RETVAL = new Flow(Flow::new_from_config_width(role, optwidth, nozzle_diameter, height, bridge_flow_ratio)); + RETVAL = new Flow(Flow::new_from_config_width(role, optwidth, nozzle_diameter, height)); OUTPUT: RETVAL diff --git a/xs/xsp/Layer.xsp b/xs/xsp/Layer.xsp index 5d006e676f..50ddfd9a19 100644 --- a/xs/xsp/Layer.xsp +++ b/xs/xsp/Layer.xsp @@ -23,8 +23,8 @@ Ref fills() %code%{ RETVAL = &THIS->fills; %}; - Clone flow(FlowRole role, bool bridge = false, double width = -1) - %code%{ RETVAL = THIS->flow(role, bridge, width); %}; + Clone flow(FlowRole role) + %code%{ RETVAL = THIS->flow(role); %}; void prepare_fill_surfaces(); void make_perimeters(SurfaceCollection* slices, SurfaceCollection* fill_surfaces) %code%{ THIS->make_perimeters(*slices, fill_surfaces); %}; diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index cc3dac2247..9e632bd534 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -33,9 +33,6 @@ _constant() Ref config() %code%{ RETVAL = &THIS->config(); %}; Ref print(); - - Clone flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, PrintObject* object) - %code%{ RETVAL = THIS->flow(role, layer_height, bridge, first_layer, width, *object); %}; }; %name{Slic3r::Print::Object} class PrintObject { From f01f02154c617cc24835475da419ed6322f2ceec Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 9 Mar 2021 12:30:31 +0100 Subject: [PATCH 101/285] Reworked the "new" bridging to respect the bridge_flow_ratio by maintaining extrusion spacing, but modifying the extrusion width and / or height. --- src/libslic3r/Fill/Fill.cpp | 33 ++++---- src/libslic3r/Flow.cpp | 114 +++++++++++++++------------ src/libslic3r/Flow.hpp | 64 +++++++++------ src/libslic3r/Layer.hpp | 1 + src/libslic3r/LayerRegion.cpp | 23 +++++- src/libslic3r/PerimeterGenerator.cpp | 2 +- src/libslic3r/Print.hpp | 1 - src/libslic3r/PrintObject.cpp | 15 ++-- src/libslic3r/PrintRegion.cpp | 11 --- src/libslic3r/SupportMaterial.cpp | 40 +++++----- src/libslic3r/SupportMaterial.hpp | 1 + xs/xsp/Flow.xsp | 2 - 12 files changed, 173 insertions(+), 134 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 129a9440cf..579259a5fc 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -28,6 +28,8 @@ struct SurfaceFillParams // coordf_t overlap = 0.; // Angle as provided by the region config, in radians. float angle = 0.f; + // Is bridging used for this fill? Bridging parameters may be used even if this->flow.bridge() is not set. + bool bridge; // Non-negative for a bridge. float bridge_angle = 0.f; @@ -73,18 +75,19 @@ struct SurfaceFillParams RETURN_COMPARE_NON_EQUAL(flow.width()); RETURN_COMPARE_NON_EQUAL(flow.height()); RETURN_COMPARE_NON_EQUAL(flow.nozzle_diameter()); - RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, flow.bridge()); + RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, bridge); RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, extrusion_role); return false; } bool operator==(const SurfaceFillParams &rhs) const { return this->extruder == rhs.extruder && - this->pattern == rhs.pattern && this->pattern == rhs.pattern && this->spacing == rhs.spacing && // this->overlap == rhs.overlap && this->angle == rhs.angle && + this->bridge == rhs.bridge && +// this->bridge_angle == rhs.bridge_angle && this->density == rhs.density && // this->dont_adjust == rhs.dont_adjust && this->anchor_length == rhs.anchor_length && @@ -128,6 +131,7 @@ std::vector group_fills(const Layer &layer) if (surface.is_solid()) { params.density = 100.f; + //FIXME for non-thick bridges, shall we allow a bottom surface pattern? params.pattern = (surface.is_external() && ! is_bridge) ? (surface.is_top() ? region_config.top_fill_pattern.value : region_config.bottom_fill_pattern.value) : region_config.top_fill_pattern == ipMonotonic ? ipMonotonic : ipRectilinear; @@ -144,9 +148,10 @@ std::vector group_fills(const Layer &layer) params.angle = float(Geometry::deg2rad(region_config.fill_angle.value)); // Calculate the actual flow we'll be using for this infill. - params.flow = is_bridge || Fill::use_bridge_flow(params.pattern) ? + params.bridge = is_bridge || Fill::use_bridge_flow(params.pattern); + params.flow = params.bridge ? layerm.bridging_flow(extrusion_role) : - layerm.region()->flow(*layer.object(), extrusion_role, (surface.thickness == -1) ? layer.height : surface.thickness, layer.id() == 0); + layerm.flow(extrusion_role, (surface.thickness == -1) ? layer.height : surface.thickness); // Calculate flow spacing for infill pattern generation. if (surface.is_solid() || is_bridge) { @@ -159,7 +164,7 @@ std::vector group_fills(const Layer &layer) // for all layers, for avoiding the ugly effect of // misaligned infill on first layer because of different extrusion width and // layer height - params.spacing = layerm.region()->flow(*layer.object(), frInfill, layer.object()->config().layer_height).spacing(); + params.spacing = layerm.flow(frInfill, layer.object()->config().layer_height).spacing(); // Anchor a sparse infill to inner perimeters with the following anchor length: params.anchor_length = float(region_config.infill_anchor); if (region_config.infill_anchor.percent) @@ -278,7 +283,7 @@ std::vector group_fills(const Layer &layer) params.extrusion_role = erInternalInfill; params.angle = float(Geometry::deg2rad(layerm.region()->config().fill_angle.value)); // calculate the actual flow we'll be using for this infill - params.flow = layerm.region()->flow(*layer.object(), frSolidInfill, layer.height, layer.id() == 0); + params.flow = layerm.flow(frSolidInfill); params.spacing = params.flow.spacing(); surface_fills.emplace_back(params); surface_fills.back().surface.surface_type = stInternalSolid; @@ -346,9 +351,9 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree; // calculate flow spacing for infill pattern generation - bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.flow.bridge(); + bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.bridge; double link_max_length = 0.; - if (! surface_fill.params.flow.bridge()) { + if (! surface_fill.params.bridge) { #if 0 link_max_length = layerm.region()->config().get_abs_value(surface.is_external() ? "external_fill_link_max_length" : "fill_link_max_length", flow.spacing()); // printf("flow spacing: %f, is_external: %d, link_max_length: %lf\n", flow.spacing(), int(surface.is_external()), link_max_length); @@ -389,9 +394,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: // so we can safely ignore the slight variation that might have // been applied to f->spacing } else { - Flow new_flow = surface_fill.params.flow.bridge() ? - Flow::bridging_flow_from_spacing(float(f->spacing), surface_fill.params.flow.nozzle_diameter()) : - Flow::new_from_spacing(float(f->spacing), surface_fill.params.flow.nozzle_diameter(), surface_fill.params.flow.height()); + Flow new_flow = surface_fill.params.flow.with_spacing(float(f->spacing)); flow_mm3_per_mm = new_flow.mm3_per_mm(); flow_width = new_flow.width(); } @@ -601,9 +604,9 @@ void Layer::make_ironing() fill.spacing = ironing_params.line_spacing; fill.angle = float(ironing_params.angle + 0.25 * M_PI); fill.link_max_length = (coord_t)scale_(3. * fill.spacing); - double height = ironing_params.height * fill.spacing / nozzle_dmr; - Flow flow = Flow::new_from_spacing(float(nozzle_dmr), 0., float(height)); - double flow_mm3_per_mm = flow.mm3_per_mm(); + double extrusion_height = ironing_params.height * fill.spacing / nozzle_dmr; + float extrusion_width = Flow::rounded_rectangle_extrusion_width_from_spacing(float(nozzle_dmr), float(extrusion_height)); + double flow_mm3_per_mm = nozzle_dmr * extrusion_height; Surface surface_fill(stTop, ExPolygon()); for (ExPolygon &expoly : ironing_areas) { surface_fill.expolygon = std::move(expoly); @@ -621,7 +624,7 @@ void Layer::make_ironing() extrusion_entities_append_paths( eec->entities, std::move(polylines), erIroning, - flow_mm3_per_mm, float(flow.width()), float(height)); + flow_mm3_per_mm, extrusion_width, float(extrusion_height)); } } } diff --git a/src/libslic3r/Flow.cpp b/src/libslic3r/Flow.cpp index 103123cdb2..6e54a517ca 100644 --- a/src/libslic3r/Flow.cpp +++ b/src/libslic3r/Flow.cpp @@ -6,12 +6,6 @@ #include -// Overlap factor of perimeter lines. Currently no overlap. -// #define HAS_PERIMETER_LINE_OVERLAP -#ifdef HAS_PERIMETER_LINE_OVERLAP - #define PERIMETER_LINE_OVERLAP_FACTOR 1.0 -#endif - // Mark string for localization and translate. #define L(s) Slic3r::I18N::translate(s) @@ -142,62 +136,82 @@ Flow Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent w = float(width.get_abs_value(height)); } - return Flow(w, height, nozzle_diameter, false); + return Flow(w, height, rounded_rectangle_extrusion_spacing(w, height), nozzle_diameter, false); } -// This constructor builds a Flow object from a given centerline spacing. -Flow Flow::new_from_spacing(float spacing, float nozzle_diameter, float height) +// Adjust extrusion flow for new extrusion line spacing, maintaining the old spacing between extrusions. +Flow Flow::with_spacing(float new_spacing) const { - if (height <= 0) - throw Slic3r::InvalidArgument("Invalid flow height supplied to new_from_spacing()"); - // Calculate width from spacing. - // For normal extrusons, extrusion width is wider than the spacing due to the rounding and squishing of the extrusions. - float width = float( -#ifdef HAS_PERIMETER_LINE_OVERLAP - (spacing + PERIMETER_LINE_OVERLAP_FACTOR * height * (1. - 0.25 * PI)); -#else - (spacing + height * (1. - 0.25 * PI))); -#endif - return Flow(width, height, nozzle_diameter); + Flow out = *this; + if (m_bridge) { + // Diameter of the rounded extrusion. + assert(m_width == m_height); + float gap = m_spacing - m_width; + auto new_diameter = new_spacing - gap; + out.m_width = out.m_height = new_diameter; + } else { + out.m_width += new_spacing - m_spacing; + if (out.m_width < out.m_height) + throw Slic3r::InvalidArgument("Invalid spacing supplied to Flow::with_spacing()"); + } + out.m_spacing = new_spacing; + return out; } -// This method returns the centerline spacing between two adjacent extrusions -// having the same extrusion width (and other properties). -float Flow::spacing() const +// Adjust the width / height of a rounded extrusion model to reach the prescribed cross section area while maintaining extrusion spacing. +Flow Flow::with_cross_section(float area_new) const { -#ifdef HAS_PERIMETER_LINE_OVERLAP - if (m_bridge) - return m_width + BRIDGE_EXTRA_SPACING; - // rectangle with semicircles at the ends - float min_flow_spacing = m_width - m_height * (1. - 0.25 * PI); - float res = m_width - PERIMETER_LINE_OVERLAP_FACTOR * (m_width - min_flow_spacing); -#else - float res = float(m_bridge ? (m_width + BRIDGE_EXTRA_SPACING) : (m_width - m_height * (1. - 0.25 * PI))); -#endif -// assert(res > 0.f); - if (res <= 0.f) - throw FlowErrorNegativeSpacing(); - return res; + assert(! m_bridge); + assert(flow.width() >= flow.height()); + + // Adjust for bridge_flow_ratio, maintain the extrusion spacing. + float area = this->mm3_per_mm(); + if (area_new > area + EPSILON) { + // Increasing the flow rate. + float new_full_spacing = area_new / m_height; + if (new_full_spacing > m_spacing) { + // Filling up the spacing without an air gap. Grow the extrusion in height. + float height = area_new / m_spacing; + return Flow(rounded_rectangle_extrusion_width_from_spacing(m_spacing, height), height, m_spacing, m_nozzle_diameter, false); + } else { + return this->with_width(rounded_rectangle_extrusion_width_from_spacing(area / m_height, m_height)); + } + } else if (area_new < area - EPSILON) { + // Decreasing the flow rate. + float width_new = m_width - (area - area_new) / m_height; + assert(width_dif > 0); + if (width_new > m_height) { + // Shrink the extrusion width. + return this->with_width(width_new); + } else { + // Create a rounded extrusion. + auto dmr = float(sqrt(area_new / M_PI)); + return Flow(dmr, dmr, m_spacing, m_nozzle_diameter, false); + } + } else + return *this; } -// This method returns the centerline spacing between an extrusion using this -// flow and another one using another flow. -// this->spacing(other) shall return the same value as other.spacing(*this) -float Flow::spacing(const Flow &other) const +float Flow::rounded_rectangle_extrusion_spacing(float width, float height) { - assert(m_height == other.m_height); - assert(m_bridge == other.m_bridge); - float res = float(m_bridge ? - 0.5 * m_width + 0.5 * other.m_width + BRIDGE_EXTRA_SPACING : - 0.5 * this->spacing() + 0.5 * other.spacing()); -// assert(res > 0.f); - if (res <= 0.f) - throw FlowErrorNegativeSpacing(); - return res; + auto out = width - height * float(1. - 0.25 * PI); + if (out <= 0.f) + throw FlowErrorNegativeSpacing(); + return out; +} + +float Flow::rounded_rectangle_extrusion_width_from_spacing(float spacing, float height) +{ + return float(spacing + height * (1. - 0.25 * PI)); +} + +float Flow::bridge_extrusion_spacing(float dmr) +{ + return dmr + BRIDGE_EXTRA_SPACING; } // This method returns extrusion volume per head move unit. -double Flow::mm3_per_mm() const +double Flow::mm3_per_mm() const { float res = m_bridge ? // Area of a circle with dmr of this->width. diff --git a/src/libslic3r/Flow.hpp b/src/libslic3r/Flow.hpp index 101dc68809..937fb176ab 100644 --- a/src/libslic3r/Flow.hpp +++ b/src/libslic3r/Flow.hpp @@ -51,26 +51,26 @@ public: class Flow { public: + Flow() = default; + Flow(float width, float height, float nozzle_diameter) : + Flow(width, height, rounded_rectangle_extrusion_spacing(width, height), nozzle_diameter, false) {} + // Non bridging flow: Maximum width of an extrusion with semicircles at the ends. // Bridging flow: Bridge thread diameter. - float width() const { return m_width; } + float width() const { return m_width; } + coord_t scaled_width() const { return coord_t(scale_(m_width)); } // Non bridging flow: Layer height. // Bridging flow: Bridge thread diameter = layer height. - float height() const { return m_height; } + float height() const { return m_height; } + // Spacing between the extrusion centerlines. + float spacing() const { return m_spacing; } + coord_t scaled_spacing() const { return coord_t(scale_(m_spacing)); } // Nozzle diameter. - float nozzle_diameter() const { return m_nozzle_diameter; } + float nozzle_diameter() const { return m_nozzle_diameter; } // Is it a bridge? - bool bridge() const { return m_bridge; } - - Flow() = default; - Flow(float w, float h, float nozzle_diameter) : Flow(w, h, nozzle_diameter, false) {} - - float spacing() const; - float spacing(const Flow &other) const; - double mm3_per_mm() const; - coord_t scaled_width() const { return coord_t(scale_(m_width)); } - coord_t scaled_spacing() const { return coord_t(scale_(this->spacing())); } - coord_t scaled_spacing(const Flow &other) const { return coord_t(scale_(this->spacing(other))); } + bool bridge() const { return m_bridge; } + // Cross section area of the extrusion. + double mm3_per_mm() const; // Elephant foot compensation spacing to be used to detect narrow parts, where the elephant foot compensation cannot be applied. // To be used on frExternalPerimeter only. @@ -80,18 +80,30 @@ public: bool operator==(const Flow &rhs) const { return m_width == rhs.m_width && m_height == rhs.m_height && m_nozzle_diameter == rhs.m_nozzle_diameter && m_bridge == rhs.m_bridge; } - Flow with_width (float width) const { assert(! m_bridge); return Flow(width, m_height, m_nozzle_diameter, m_bridge); } - Flow with_height(float height) const { assert(! m_bridge); return Flow(m_width, height, m_nozzle_diameter, m_bridge); } + Flow with_width (float width) const { + assert(! m_bridge); + return Flow(width, m_height, rounded_rectangle_extrusion_spacing(width, m_height), m_nozzle_diameter, m_bridge); + } + Flow with_height(float height) const { + assert(! m_bridge); + return Flow(m_width, height, rounded_rectangle_extrusion_spacing(m_width, height), m_nozzle_diameter, m_bridge); + } + // Adjust extrusion flow for new extrusion line spacing, maintaining the old spacing between extrusions. + Flow with_spacing(float spacing) const; + // Adjust the width / height of a rounded extrusion model to reach the prescribed cross section area while maintaining extrusion spacing. + Flow with_cross_section(float area) const; + Flow with_flow_ratio(double ratio) const { return this->with_cross_section(this->mm3_per_mm() * ratio); } + + static Flow bridging_flow(float dmr, float nozzle_diameter) { return Flow { dmr, dmr, bridge_extrusion_spacing(dmr), nozzle_diameter, true }; } - static Flow bridging_flow(float dmr, float nozzle_diameter) { return Flow { dmr, dmr, nozzle_diameter, true }; } - static Flow bridging_flow_from_spacing(float spacing, float nozzle_diameter) - { auto dmr = spacing - float(BRIDGE_EXTRA_SPACING); return Flow { dmr, dmr, nozzle_diameter, true }; } - static Flow new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height); - // Create a flow from the spacing of extrusion lines. - // This method is used exclusively to calculate new flow of 100% infill, where the extrusion width was allowed to scale - // to fit a region with integer number of lines. - static Flow new_from_spacing(float spacing, float nozzle_diameter, float height); + + // Spacing of extrusions with rounded extrusion model. + static float rounded_rectangle_extrusion_spacing(float width, float height); + // Width of extrusions with rounded extrusion model. + static float rounded_rectangle_extrusion_width_from_spacing(float spacing, float height); + // Spacing of round thread extrusions. + static float bridge_extrusion_spacing(float dmr); // Sane extrusion width defautl based on nozzle diameter. // The defaults were derived from manual Prusa MK3 profiles. @@ -104,10 +116,12 @@ public: static double extrusion_width(const std::string &opt_key, const ConfigOptionResolver &config, const unsigned int first_printing_extruder = 0); private: - Flow(float w, float h, float nozzle_diameter, bool bridge) : m_width(w), m_height(h), m_nozzle_diameter(nozzle_diameter), m_bridge(bridge) {} + Flow(float width, float height, float spacing, float nozzle_diameter, bool bridge) : + m_width(width), m_height(height), m_spacing(spacing), m_nozzle_diameter(nozzle_diameter), m_bridge(bridge) {} float m_width { 0 }; float m_height { 0 }; + float m_spacing { 0 }; float m_nozzle_diameter { 0 }; bool m_bridge { false }; }; diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index ef3124dcb8..8e23485302 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -60,6 +60,7 @@ public: ExtrusionEntityCollection fills; Flow flow(FlowRole role) const; + Flow flow(FlowRole role, double layer_height) const; Flow bridging_flow(FlowRole role) const; void slices_to_fill_surfaces_clipped(); diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 4b8fcaef90..1bca95ca37 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -17,12 +17,29 @@ namespace Slic3r { Flow LayerRegion::flow(FlowRole role) const { - return m_region->flow(*m_layer->object(), role, m_layer->height, m_layer->id() == 0); + return this->flow(role, m_layer->height); +} + +Flow LayerRegion::flow(FlowRole role, double layer_height) const +{ + return m_region->flow(*m_layer->object(), role, layer_height, m_layer->id() == 0); } Flow LayerRegion::bridging_flow(FlowRole role) const -{ - return this->layer()->object()->config().thick_bridges ? m_region->bridging_flow(role) : this->flow(role); +{ + const PrintRegion ®ion = *this->region(); + const PrintRegionConfig ®ion_config = region.config(); + if (this->layer()->object()->config().thick_bridges) { + // The old Slic3r way (different from all other slicers): Use rounded extrusions. + // Get the configured nozzle_diameter for the extruder associated to the flow role requested. + // Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will follback to zero'th element, so everything is all right. + auto nozzle_diameter = float(region.print()->config().nozzle_diameter.get_at(region.extruder(role) - 1)); + // Applies default bridge spacing. + return Flow::bridging_flow(float(sqrt(region_config.bridge_flow_ratio)) * nozzle_diameter, nozzle_diameter); + } else { + // The same way as other slicers: Use normal extrusions. Apply bridge_flow_ratio while maintaining the original spacing. + return this->flow(role).with_flow_ratio(region_config.bridge_flow_ratio); + } } // Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces. diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index d33e1cfd47..6ec4dbf6b8 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -286,7 +286,7 @@ void PerimeterGenerator::process() m_ext_mm3_per_mm = this->ext_perimeter_flow.mm3_per_mm(); coord_t ext_perimeter_width = this->ext_perimeter_flow.scaled_width(); coord_t ext_perimeter_spacing = this->ext_perimeter_flow.scaled_spacing(); - coord_t ext_perimeter_spacing2 = this->ext_perimeter_flow.scaled_spacing(this->perimeter_flow); + coord_t ext_perimeter_spacing2 = scaled(0.5f * (this->ext_perimeter_flow.spacing() + this->perimeter_flow.spacing())); // overhang perimeters m_mm3_per_mm_overhang = this->overhang_flow.mm3_per_mm(); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 04cf58fa93..91f86d0105 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -66,7 +66,6 @@ public: // 1-based extruder identifier for this region and role. unsigned int extruder(FlowRole role) const; Flow flow(const PrintObject &object, FlowRole role, double layer_height, bool first_layer = false) const; - Flow bridging_flow(FlowRole role) const; // Average diameter of nozzles participating on extruding this region. coordf_t nozzle_dmr_avg(const PrintConfig &print_config) const; // Average diameter of nozzles participating on extruding this region. diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 5939555765..3fe5f2421a 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1457,19 +1457,18 @@ void PrintObject::bridge_over_infill() const PrintRegion ®ion = *m_print->regions()[region_id]; // skip bridging in case there are no voids - if (region.config().fill_density.value == 100) continue; - - // get bridge flow - Flow bridge_flow = region.bridging_flow(frSolidInfill); - + if (region.config().fill_density.value == 100) + continue; + for (LayerPtrs::iterator layer_it = m_layers.begin(); layer_it != m_layers.end(); ++ layer_it) { // skip first layer if (layer_it == m_layers.begin()) continue; - Layer* layer = *layer_it; - LayerRegion* layerm = layer->m_regions[region_id]; - + Layer *layer = *layer_it; + LayerRegion *layerm = layer->m_regions[region_id]; + Flow bridge_flow = layerm->bridging_flow(frSolidInfill); + // extract the stInternalSolid surfaces that might be transformed into bridges Polygons internal_solid; layerm->fill_surfaces.filter_by_type(stInternalSolid, &internal_solid); diff --git a/src/libslic3r/PrintRegion.cpp b/src/libslic3r/PrintRegion.cpp index acb4298212..837200984a 100644 --- a/src/libslic3r/PrintRegion.cpp +++ b/src/libslic3r/PrintRegion.cpp @@ -48,17 +48,6 @@ Flow PrintRegion::flow(const PrintObject &object, FlowRole role, double layer_he return Flow::new_from_config_width(role, config_width, nozzle_diameter, float(layer_height)); } -Flow PrintRegion::bridging_flow(FlowRole role) const -{ - // Get the configured nozzle_diameter for the extruder associated to the flow role requested. - // Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will follback to zero'th element, so everything is all right. - auto nozzle_diameter = float(m_print->config().nozzle_diameter.get_at(this->extruder(role) - 1)); - double bfr = m_config.bridge_flow_ratio; - if (bfr != 1.) - bfr = sqrt(bfr); - return Flow::bridging_flow(float(bfr) * nozzle_diameter, nozzle_diameter); -} - coordf_t PrintRegion::nozzle_dmr_avg(const PrintConfig &print_config) const { return (print_config.nozzle_diameter.get_at(m_config.perimeter_extruder.value - 1) + diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 7c018c9747..b68d1f2c30 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -334,7 +334,7 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object for (auto lh : m_print_config->min_layer_height.values) m_support_layer_height_min = std::min(m_support_layer_height_min, std::max(0.01, lh)); - if (m_object_config->support_material_interface_layers.value == 0) { + if (m_slicing_params.soluble_interface) { // No interface layers allowed, print everything with the base support pattern. m_support_material_interface_flow = m_support_material_flow; } @@ -342,11 +342,21 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object // Evaluate the XY gap between the object outer perimeters and the support structures. // Evaluate the XY gap between the object outer perimeters and the support structures. coordf_t external_perimeter_width = 0.; + size_t num_nonempty_regions = 0; + coordf_t bridge_flow_ratio = 0; for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) - if (! object->region_volumes[region_id].empty()) - external_perimeter_width = std::max(external_perimeter_width, - (coordf_t)object->print()->get_region(region_id)->flow(*object, frExternalPerimeter, slicing_params.layer_height).width()); + if (! object->region_volumes[region_id].empty()) { + ++ num_nonempty_regions; + const PrintRegion ®ion = *object->print()->get_region(region_id); + external_perimeter_width = std::max(external_perimeter_width, coordf_t(region.flow(*object, frExternalPerimeter, slicing_params.layer_height).width())); + bridge_flow_ratio += region.config().bridge_flow_ratio; + } m_gap_xy = m_object_config->support_material_xy_spacing.get_abs_value(external_perimeter_width); + bridge_flow_ratio /= num_nonempty_regions; + + m_support_material_bottom_interface_flow = m_slicing_params.soluble_interface || ! m_object_config->thick_bridges ? + m_support_material_interface_flow.with_flow_ratio(bridge_flow_ratio) : + Flow::bridging_flow(bridge_flow_ratio * m_support_material_interface_flow.nozzle_diameter(), m_support_material_interface_flow.nozzle_diameter()); m_can_merge_support_regions = m_object_config->support_material_extruder.value == m_object_config->support_material_interface_extruder.value; if (! m_can_merge_support_regions && (m_object_config->support_material_extruder.value == 0 || m_object_config->support_material_interface_extruder.value == 0)) { @@ -1231,8 +1241,8 @@ namespace SupportMaterialInternal { // since we're dealing with bridges, we can't assume width is larger than spacing, // so we take the largest value and also apply safety offset to be ensure no gaps // are left in between - Flow bridge_flow = layerm->bridging_flow(frPerimeter); - float w = float(std::max(bridge_flow.scaled_width(), bridge_flow.scaled_spacing())); + Flow perimeter_bridge_flow = layerm->bridging_flow(frPerimeter); + float w = float(std::max(perimeter_bridge_flow.scaled_width(), perimeter_bridge_flow.scaled_spacing())); for (Polyline &polyline : overhang_perimeters) if (polyline.is_straight()) { // This is a bridge @@ -1879,13 +1889,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta layer_new.height = m_slicing_params.soluble_interface ? // Align the interface layer with the object's layer height. object.layers()[layer_id + 1]->height : - // Place a bridge flow interface layer over the top surface. - //FIXME Check whether the bottom bridging surfaces are extruded correctly (no bridging flow correction applied?) - // According to Jindrich the bottom surfaces work well. - //FIXME test the bridging flow instead? - m_object_config->thick_bridges.value ? m_support_material_interface_flow.nozzle_diameter() : - // Take the default layer height. - m_object_config->layer_height; + // Place a bridge flow interface layer or the normal flow interface layer over the top surface. + m_support_material_bottom_interface_flow.height(); layer_new.print_z = m_slicing_params.soluble_interface ? object.layers()[layer_id + 1]->print_z : layer.print_z + layer_new.height + m_object_config->support_material_contact_distance.value; layer_new.bottom_z = layer.print_z; @@ -3497,7 +3502,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( coordf_t interface_density = std::min(1., m_support_material_interface_flow.spacing() / interface_spacing); coordf_t support_spacing = m_object_config->support_material_spacing.value + m_support_material_flow.spacing(); coordf_t support_density = std::min(1., m_support_material_flow.spacing() / support_spacing); - if (m_object_config->support_material_interface_layers.value == 0) { + if (m_slicing_params.soluble_interface) { // No interface layers allowed, print everything with the base support pattern. interface_spacing = support_spacing; interface_density = support_density; @@ -3738,13 +3743,12 @@ void PrintObjectSupportMaterial::generate_toolpaths( MyLayerExtruded &layer_ex = (i == 0) ? top_contact_layer : (i == 1 ? bottom_contact_layer : interface_layer); if (layer_ex.empty() || layer_ex.polygons_to_extrude().empty()) continue; - bool interface_as_base = (&layer_ex == &interface_layer) && m_object_config->support_material_interface_layers.value == 0; + bool interface_as_base = (&layer_ex == &interface_layer) && m_slicing_params.soluble_interface; //FIXME Bottom interfaces are extruded with the briding flow. Some bridging layers have its height slightly reduced, therefore // the bridging flow does not quite apply. Reduce the flow to area of an ellipse? (A = pi * a * b) auto interface_flow = layer_ex.layer->bridging ? - Flow::bridging_flow(layer_ex.layer->height, m_support_material_interface_flow.nozzle_diameter()) : - Flow(interface_as_base ? m_support_material_flow.width() : m_support_material_interface_flow.width(), - float(layer_ex.layer->height), m_support_material_interface_flow.nozzle_diameter()); + Flow::bridging_flow(layer_ex.layer->height, m_support_material_bottom_interface_flow.nozzle_diameter()) : + (interface_as_base ? &m_support_material_flow : &m_support_material_interface_flow)->with_height(float(layer_ex.layer->height)); filler_interface->angle = interface_as_base ? // If zero interface layers are configured, use the same angle as for the base layers. angles[support_layer_id % angles.size()] : diff --git a/src/libslic3r/SupportMaterial.hpp b/src/libslic3r/SupportMaterial.hpp index 7123290a62..da13768f60 100644 --- a/src/libslic3r/SupportMaterial.hpp +++ b/src/libslic3r/SupportMaterial.hpp @@ -244,6 +244,7 @@ private: Flow m_first_layer_flow; Flow m_support_material_flow; Flow m_support_material_interface_flow; + Flow m_support_material_bottom_interface_flow; // Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder? bool m_can_merge_support_regions; diff --git a/xs/xsp/Flow.xsp b/xs/xsp/Flow.xsp index 6962085d51..019af16f29 100644 --- a/xs/xsp/Flow.xsp +++ b/xs/xsp/Flow.xsp @@ -16,8 +16,6 @@ float nozzle_diameter(); bool bridge(); float spacing(); - float spacing_to(Flow* other) - %code{% RETVAL = THIS->spacing(*other); %}; int scaled_width(); int scaled_spacing(); double mm3_per_mm(); From adcbe4347ccd485a273f81687b8456c63784e0c3 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 9 Mar 2021 13:54:42 +0100 Subject: [PATCH 102/285] Fixed unit tests. --- src/libslic3r/Flow.cpp | 5 +++-- src/libslic3r/Flow.hpp | 3 ++- src/libslic3r/GCode.cpp | 3 ++- tests/fff_print/test_flow.cpp | 6 +++--- tests/fff_print/test_print.cpp | 14 +++++++------- tests/fff_print/test_printgcode.cpp | 13 +++++++------ 6 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/libslic3r/Flow.cpp b/src/libslic3r/Flow.cpp index 6e54a517ca..1645bf683a 100644 --- a/src/libslic3r/Flow.cpp +++ b/src/libslic3r/Flow.cpp @@ -150,6 +150,7 @@ Flow Flow::with_spacing(float new_spacing) const auto new_diameter = new_spacing - gap; out.m_width = out.m_height = new_diameter; } else { + assert(m_width >= m_height); out.m_width += new_spacing - m_spacing; if (out.m_width < out.m_height) throw Slic3r::InvalidArgument("Invalid spacing supplied to Flow::with_spacing()"); @@ -162,7 +163,7 @@ Flow Flow::with_spacing(float new_spacing) const Flow Flow::with_cross_section(float area_new) const { assert(! m_bridge); - assert(flow.width() >= flow.height()); + assert(m_width >= m_height); // Adjust for bridge_flow_ratio, maintain the extrusion spacing. float area = this->mm3_per_mm(); @@ -179,7 +180,7 @@ Flow Flow::with_cross_section(float area_new) const } else if (area_new < area - EPSILON) { // Decreasing the flow rate. float width_new = m_width - (area - area_new) / m_height; - assert(width_dif > 0); + assert(width_new > 0); if (width_new > m_height) { // Shrink the extrusion width. return this->with_width(width_new); diff --git a/src/libslic3r/Flow.hpp b/src/libslic3r/Flow.hpp index 937fb176ab..428d31c1f7 100644 --- a/src/libslic3r/Flow.hpp +++ b/src/libslic3r/Flow.hpp @@ -117,7 +117,8 @@ public: private: Flow(float width, float height, float spacing, float nozzle_diameter, bool bridge) : - m_width(width), m_height(height), m_spacing(spacing), m_nozzle_diameter(nozzle_diameter), m_bridge(bridge) {} + m_width(width), m_height(height), m_spacing(spacing), m_nozzle_diameter(nozzle_diameter), m_bridge(bridge) + { assert(width >= height); } float m_width { 0 }; float m_height { 0 }; diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index fd6c82b84f..28a68e1715 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1815,7 +1815,8 @@ namespace Skirt { // Extrude skirt at the print_z of the raft layers and normal object layers // not at the print_z of the interlaced support material layers. std::map> skirt_loops_per_extruder_out; - assert(skirt_done.empty()); + //For sequential print, the following test may fail when extruding the 2nd and other objects. + // assert(skirt_done.empty()); if (skirt_done.empty() && print.has_skirt() && ! print.skirt().entities.empty() && layer_tools.has_skirt) { skirt_loops_per_extruder_all_printing(print, layer_tools, skirt_loops_per_extruder_out); skirt_done.emplace_back(layer_tools.print_z); diff --git a/tests/fff_print/test_flow.cpp b/tests/fff_print/test_flow.cpp index b679a07e35..08ba15a84a 100644 --- a/tests/fff_print/test_flow.cpp +++ b/tests/fff_print/test_flow.cpp @@ -96,7 +96,7 @@ SCENARIO("Flow: Flow math for non-bridges", "[Flow]") { GIVEN("Nozzle Diameter of 0.4, a desired width of 1mm and layer height of 0.5") { ConfigOptionFloatOrPercent width(1.0, false); float nozzle_diameter = 0.4f; - float layer_height = 0.5f; + float layer_height = 0.4f; // Spacing for non-bridges is has some overlap THEN("External perimeter flow has spacing fixed to 1.125 * nozzle_diameter") { @@ -126,8 +126,8 @@ SCENARIO("Flow: Flow math for non-bridges", "[Flow]") { REQUIRE(flow.width() == Approx(1.125 * nozzle_diameter)); } } - WHEN("Layer height is set to 0.2") { - layer_height = 0.3f; + WHEN("Layer height is set to 0.25") { + layer_height = 0.25f; THEN("Min width is set.") { auto flow = Flow::new_from_config_width(frPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height); REQUIRE(flow.width() == Approx(1.125 * nozzle_diameter)); diff --git a/tests/fff_print/test_print.cpp b/tests/fff_print/test_print.cpp index 3250443ed0..fc2ac3dee2 100644 --- a/tests/fff_print/test_print.cpp +++ b/tests/fff_print/test_print.cpp @@ -53,8 +53,8 @@ SCENARIO("Print: Changing number of solid surfaces does not cause all surfaces t config.set_deserialize({ { "top_solid_layers", 2 }, { "bottom_solid_layers", 1 }, - { "layer_height", 0.5 }, // get a known number of layers - { "first_layer_height", 0.5 } + { "layer_height", 0.25 }, // get a known number of layers + { "first_layer_height", 0.25 } }); Slic3r::Print print; Slic3r::Model model; @@ -72,8 +72,8 @@ SCENARIO("Print: Changing number of solid surfaces does not cause all surfaces t }; print.process(); test_is_solid_infill(0, 0); // should be solid - test_is_solid_infill(0, 39); // should be solid - test_is_solid_infill(0, 38); // should be solid + test_is_solid_infill(0, 79); // should be solid + test_is_solid_infill(0, 78); // should be solid WHEN("Model is re-sliced with top_solid_layers == 3") { config.set("top_solid_layers", 3); print.apply(model, config); @@ -82,9 +82,9 @@ SCENARIO("Print: Changing number of solid surfaces does not cause all surfaces t test_is_solid_infill(0, 0); } AND_THEN("Print object has 3 top solid layers") { - test_is_solid_infill(0, 39); - test_is_solid_infill(0, 38); - test_is_solid_infill(0, 37); + test_is_solid_infill(0, 79); + test_is_solid_infill(0, 78); + test_is_solid_infill(0, 77); } } } diff --git a/tests/fff_print/test_printgcode.cpp b/tests/fff_print/test_printgcode.cpp index 10d3af9a31..d6f6eca58a 100644 --- a/tests/fff_print/test_printgcode.cpp +++ b/tests/fff_print/test_printgcode.cpp @@ -244,23 +244,24 @@ SCENARIO( "PrintGCode basic functionality", "[PrintGCode]") { { "complete_objects", true }, { "gcode_comments", true }, { "layer_gcode", ";Layer:[layer_num] ([layer_z] mm)" }, - { "layer_height", 1.0 }, - { "first_layer_height", 1.0 } + { "layer_height", 0.1 }, + { "first_layer_height", 0.1 } }); // End of the 1st object. - size_t pos = gcode.find(";Layer:19 "); + std::string token = ";Layer:199 "; + size_t pos = gcode.find(token); THEN("First and second object last layer is emitted") { // First object REQUIRE(pos != std::string::npos); - pos += 10; + pos += token.size(); REQUIRE(pos < gcode.size()); double z = 0; REQUIRE((sscanf(gcode.data() + pos, "(%lf mm)", &z) == 1)); REQUIRE(z == Approx(20.)); // Second object - pos = gcode.find(";Layer:39 ", pos); + pos = gcode.find(";Layer:399 ", pos); REQUIRE(pos != std::string::npos); - pos += 10; + pos += token.size(); REQUIRE(pos < gcode.size()); REQUIRE((sscanf(gcode.data() + pos, "(%lf mm)", &z) == 1)); REQUIRE(z == Approx(20.)); From 00db3dc41937670e91c26170c9c168bf75a86aaf Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 9 Mar 2021 15:29:13 +0100 Subject: [PATCH 103/285] WIP: Splitting the number of top / bottom support interface layers. If the new support_material_bottom_interface_layers is left at default -1, then support_material_interface_layers is used for both top and bottom interface layers. If support_material_interface_layers == 0, then neither top nor bottom interface layers are being extruded. --- src/libslic3r/Flow.hpp | 5 +- src/libslic3r/Preset.cpp | 2 +- src/libslic3r/PrintConfig.cpp | 12 ++- src/libslic3r/PrintConfig.hpp | 2 + src/libslic3r/PrintObject.cpp | 1 + src/libslic3r/SupportMaterial.cpp | 105 +++++++++++++++----------- src/slic3r/GUI/ConfigManipulation.cpp | 2 +- src/slic3r/GUI/Tab.cpp | 1 + 8 files changed, 81 insertions(+), 49 deletions(-) diff --git a/src/libslic3r/Flow.hpp b/src/libslic3r/Flow.hpp index 428d31c1f7..04ced3e13a 100644 --- a/src/libslic3r/Flow.hpp +++ b/src/libslic3r/Flow.hpp @@ -118,7 +118,10 @@ public: private: Flow(float width, float height, float spacing, float nozzle_diameter, bool bridge) : m_width(width), m_height(height), m_spacing(spacing), m_nozzle_diameter(nozzle_diameter), m_bridge(bridge) - { assert(width >= height); } + { + // Gap fill violates this condition. + //assert(width >= height); + } float m_width { 0 }; float m_height { 0 }; diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 81a14eaec9..412dbc6785 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -428,7 +428,7 @@ const std::vector& Preset::print_options() "min_skirt_length", "brim_width", "brim_offset", "brim_type", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers", "raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion", "support_material_pattern", "support_material_with_sheath", "support_material_spacing", - "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", + "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", "support_material_bottom_interface_layers", "support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance", "support_material_buildplate_only", "dont_support_bridges", "thick_bridges", "notes", "complete_objects", "extruder_clearance_radius", "extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "perimeter_extruder", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index b87042b897..d744cb2a75 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2299,7 +2299,7 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionInt(1)); def = this->add("support_material_interface_layers", coInt); - def->label = L("Interface layers"); + def->label = L("Top interface layers"); def->category = L("Support material"); def->tooltip = L("Number of interface layers to insert between the object(s) and support material."); def->sidetext = L("layers"); @@ -2307,6 +2307,16 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionInt(3)); + def = this->add("support_material_bottom_interface_layers", coInt); + def->label = L("Bottom interface layers"); + def->category = L("Support material"); + def->tooltip = L("Number of interface layers to insert between the object(s) and support material. " + "Set to -1 to use support_material_interface_layers"); + def->sidetext = L("layers"); + def->min = -1; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionInt(-1)); + def = this->add("support_material_interface_spacing", coFloat); def->label = L("Interface pattern spacing"); def->category = L("Support material"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index c92abf51f7..4a4647b736 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -509,6 +509,7 @@ public: ConfigOptionBool support_material_interface_contact_loops; ConfigOptionInt support_material_interface_extruder; ConfigOptionInt support_material_interface_layers; + ConfigOptionInt support_material_bottom_interface_layers; // Spacing between interface lines (the hatching distance). Set zero to get a solid interface. ConfigOptionFloat support_material_interface_spacing; ConfigOptionFloatOrPercent support_material_interface_speed; @@ -560,6 +561,7 @@ protected: OPT_PTR(support_material_extrusion_width); OPT_PTR(support_material_interface_extruder); OPT_PTR(support_material_interface_layers); + OPT_PTR(support_material_bottom_interface_layers); OPT_PTR(support_material_interface_spacing); OPT_PTR(support_material_interface_speed); OPT_PTR(support_material_pattern); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 3fe5f2421a..a3b0fb787d 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -587,6 +587,7 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "support_material_extruder" || opt_key == "support_material_extrusion_width" || opt_key == "support_material_interface_layers" + || opt_key == "support_material_bottom_interface_layers" || opt_key == "support_material_interface_pattern" || opt_key == "support_material_interface_contact_loops" || opt_key == "support_material_interface_extruder" diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index b68d1f2c30..7747559e63 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -2720,17 +2720,23 @@ std::pairsupport_material_interface_extruder.value > 0 && m_print_config->filament_soluble.get_at(m_object_config->support_material_interface_extruder.value - 1) && // Base extruder: Either "print with active extruder" not soluble. (m_object_config->support_material_extruder.value == 0 || ! m_print_config->filament_soluble.get_at(m_object_config->support_material_extruder.value - 1)); - int num_interface_layers = m_object_config->support_material_interface_layers.value; - int num_base_interface_layers = soluble_interface_non_soluble_base ? std::min(num_interface_layers / 2, 2) : 0; + int num_interface_layers_top = m_object_config->support_material_interface_layers; + int num_interface_layers_bottom = m_object_config->support_material_bottom_interface_layers; + if (num_interface_layers_bottom < 0) + num_interface_layers_bottom = num_interface_layers_top; + int num_base_interface_layers_top = soluble_interface_non_soluble_base ? std::min(num_interface_layers_top / 2, 2) : 0; + int num_base_interface_layers_bottom = soluble_interface_non_soluble_base ? std::min(num_interface_layers_bottom / 2, 2) : 0; - if (! intermediate_layers.empty() && num_interface_layers > 1) { + if (! intermediate_layers.empty() && (num_interface_layers_top > 1 || num_interface_layers_bottom > 1)) { // For all intermediate layers, collect top contact surfaces, which are not further than support_material_interface_layers. BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - start"; // Since the intermediate layer index starts at zero the number of interface layer needs to be reduced by 1. - -- num_interface_layers; - int num_interface_layers_only = num_interface_layers - num_base_interface_layers; + -- num_interface_layers_top; + -- num_interface_layers_bottom; + int num_interface_layers_only_top = num_interface_layers_top - num_base_interface_layers_top; + int num_interface_layers_only_bottom = num_interface_layers_bottom - num_base_interface_layers_bottom; interface_layers.assign(intermediate_layers.size(), nullptr); - if (num_base_interface_layers) + if (num_base_interface_layers_top || num_base_interface_layers_bottom) base_interface_layers.assign(intermediate_layers.size(), nullptr); tbb::spin_mutex layer_storage_mutex; // Insert a new layer into base_interface_layers, if intersection with base exists. @@ -2754,7 +2760,8 @@ std::pair(0, int(intermediate_layers.size())), - [&bottom_contacts, &top_contacts, &intermediate_layers, &insert_layer, num_interface_layers, num_base_interface_layers, num_interface_layers_only, + [&bottom_contacts, &top_contacts, &intermediate_layers, &insert_layer, + num_interface_layers_top, num_interface_layers_bottom, num_base_interface_layers_top, num_base_interface_layers_bottom, num_interface_layers_only_top, num_interface_layers_only_bottom, &interface_layers, &base_interface_layers](const tbb::blocked_range& range) { // Gather the top / bottom contact layers intersecting with num_interface_layers resp. num_interface_layers_only intermediate layers above / below // this intermediate layer. @@ -2765,45 +2772,51 @@ std::pairprint_z; - coordf_t top_inteface_z = std::numeric_limits::max(); - coordf_t bottom_z = intermediate_layers[std::max(0, idx_intermediate_layer - num_interface_layers + 1)]->bottom_z; - coordf_t bottom_interface_z = - std::numeric_limits::max(); - if (num_base_interface_layers > 0) { - // Some base interface layers will be generated. - if (num_interface_layers_only == 0) - // Only base interface layers to generate. - std::swap(top_inteface_z, bottom_interface_z); - else { - top_inteface_z = intermediate_layers[std::min(num_intermediate - 1, idx_intermediate_layer + num_interface_layers_only - 1)]->print_z; - bottom_interface_z = intermediate_layers[std::max(0, idx_intermediate_layer - num_interface_layers_only)]->bottom_z; - } - } - // Move idx_top_contact_first up until above the current print_z. - idx_top_contact_first = idx_higher_or_equal(top_contacts, idx_top_contact_first, [&intermediate_layer](const MyLayer *layer){ return layer->print_z >= intermediate_layer.print_z; }); // - EPSILON - // Collect the top contact areas above this intermediate layer, below top_z. Polygons polygons_top_contact_projected_interface; Polygons polygons_top_contact_projected_base; - for (int idx_top_contact = idx_top_contact_first; idx_top_contact < int(top_contacts.size()); ++ idx_top_contact) { - const MyLayer &top_contact_layer = *top_contacts[idx_top_contact]; - //FIXME maybe this adds one interface layer in excess? - if (top_contact_layer.bottom_z - EPSILON > top_z) - break; - polygons_append(top_contact_layer.bottom_z - EPSILON > top_inteface_z ? polygons_top_contact_projected_base : polygons_top_contact_projected_interface, top_contact_layer.polygons); - } - // Move idx_bottom_contact_first up until touching bottom_z. - idx_bottom_contact_first = idx_higher_or_equal(bottom_contacts, idx_bottom_contact_first, [bottom_z](const MyLayer *layer){ return layer->print_z >= bottom_z - EPSILON; }); - // Collect the top contact areas above this intermediate layer, below top_z. Polygons polygons_bottom_contact_projected_interface; Polygons polygons_bottom_contact_projected_base; - for (int idx_bottom_contact = idx_bottom_contact_first; idx_bottom_contact < int(bottom_contacts.size()); ++ idx_bottom_contact) { - const MyLayer &bottom_contact_layer = *bottom_contacts[idx_bottom_contact]; - if (bottom_contact_layer.print_z - EPSILON > intermediate_layer.bottom_z) - break; - polygons_append(bottom_contact_layer.print_z - EPSILON > bottom_interface_z ? polygons_bottom_contact_projected_interface : polygons_bottom_contact_projected_base, bottom_contact_layer.polygons); + if (num_interface_layers_top > -1) { + // Top Z coordinate of a slab, over which we are collecting the top / bottom contact surfaces + coordf_t top_z = intermediate_layers[std::min(num_intermediate - 1, idx_intermediate_layer + num_interface_layers_top - 1)]->print_z; + coordf_t top_inteface_z = std::numeric_limits::max(); + if (num_base_interface_layers_top > 0) + // Some top base interface layers will be generated. + top_inteface_z = num_interface_layers_only_top == 0 ? + // Only base interface layers to generate. + - std::numeric_limits::max() : + intermediate_layers[std::min(num_intermediate - 1, idx_intermediate_layer + num_interface_layers_only_top - 1)]->print_z; + // Move idx_top_contact_first up until above the current print_z. + idx_top_contact_first = idx_higher_or_equal(top_contacts, idx_top_contact_first, [&intermediate_layer](const MyLayer *layer){ return layer->print_z >= intermediate_layer.print_z; }); // - EPSILON + // Collect the top contact areas above this intermediate layer, below top_z. + for (int idx_top_contact = idx_top_contact_first; idx_top_contact < int(top_contacts.size()); ++ idx_top_contact) { + const MyLayer &top_contact_layer = *top_contacts[idx_top_contact]; + //FIXME maybe this adds one interface layer in excess? + if (top_contact_layer.bottom_z - EPSILON > top_z) + break; + polygons_append(top_contact_layer.bottom_z - EPSILON > top_inteface_z ? polygons_top_contact_projected_base : polygons_top_contact_projected_interface, top_contact_layer.polygons); + } + } + if (num_interface_layers_bottom > -1) { + // Bottom Z coordinate of a slab, over which we are collecting the top / bottom contact surfaces + coordf_t bottom_z = intermediate_layers[std::max(0, idx_intermediate_layer - num_interface_layers_bottom + 1)]->bottom_z; + coordf_t bottom_interface_z = - std::numeric_limits::max(); + if (num_base_interface_layers_bottom > 0) + // Some bottom base interface layers will be generated. + bottom_interface_z = num_interface_layers_only_bottom == 0 ? + // Only base interface layers to generate. + std::numeric_limits::max() : + intermediate_layers[std::max(0, idx_intermediate_layer - num_interface_layers_only_bottom)]->bottom_z; + // Move idx_bottom_contact_first up until touching bottom_z. + idx_bottom_contact_first = idx_higher_or_equal(bottom_contacts, idx_bottom_contact_first, [bottom_z](const MyLayer *layer){ return layer->print_z >= bottom_z - EPSILON; }); + // Collect the top contact areas above this intermediate layer, below top_z. + for (int idx_bottom_contact = idx_bottom_contact_first; idx_bottom_contact < int(bottom_contacts.size()); ++ idx_bottom_contact) { + const MyLayer &bottom_contact_layer = *bottom_contacts[idx_bottom_contact]; + if (bottom_contact_layer.print_z - EPSILON > intermediate_layer.bottom_z) + break; + polygons_append(bottom_contact_layer.print_z - EPSILON > bottom_interface_z ? polygons_bottom_contact_projected_interface : polygons_bottom_contact_projected_base, bottom_contact_layer.polygons); + } } - MyLayer *interface_layer = nullptr; if (! polygons_bottom_contact_projected_interface.empty() || ! polygons_top_contact_projected_interface.empty()) { interface_layer = insert_layer( @@ -3712,10 +3725,6 @@ void PrintObjectSupportMaterial::generate_toolpaths( base_layer.merge(std::move(top_contact_layer)); else if (base_layer.empty() && !top_contact_layer.empty() && !top_contact_layer.layer->bridging) std::swap(base_layer, top_contact_layer); - if (base_layer.could_merge(bottom_contact_layer)) - base_layer.merge(std::move(bottom_contact_layer)); - else if (base_layer.empty() && !bottom_contact_layer.empty() && !bottom_contact_layer.layer->bridging) - std::swap(base_layer, bottom_contact_layer); } } else { loop_interface_processor.generate(top_contact_layer, m_support_material_interface_flow); @@ -3725,6 +3734,12 @@ void PrintObjectSupportMaterial::generate_toolpaths( if (top_contact_layer.could_merge(interface_layer)) top_contact_layer.merge(std::move(interface_layer)); } + if ((m_object_config->support_material_interface_layers == 0 || m_object_config->support_material_bottom_interface_layers == 0) && m_can_merge_support_regions) { + if (base_layer.could_merge(bottom_contact_layer)) + base_layer.merge(std::move(bottom_contact_layer)); + else if (base_layer.empty() && !bottom_contact_layer.empty() && !bottom_contact_layer.layer->bridging) + std::swap(base_layer, bottom_contact_layer); + } #if 0 if ( ! interface_layer.empty() && ! base_layer.empty()) { diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 01ec72837a..80ebe6ea80 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -286,7 +286,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) toggle_field(el, have_support_material); toggle_field("support_material_threshold", have_support_material_auto); - for (auto el : { "support_material_interface_spacing", "support_material_interface_extruder", + for (auto el : { "support_material_bottom_interface_layers", "support_material_interface_spacing", "support_material_interface_extruder", "support_material_interface_speed", "support_material_interface_contact_loops" }) toggle_field(el, have_support_material && have_support_interface); toggle_field("support_material_synchronize_layers", have_support_soluble); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 645d31f4bc..472db49761 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1512,6 +1512,7 @@ void TabPrint::build() optgroup->append_single_option_line("support_material_spacing", category_path + "pattern-spacing-0-inf"); optgroup->append_single_option_line("support_material_angle", category_path + "pattern-angle"); optgroup->append_single_option_line("support_material_interface_layers", category_path + "interface-layers"); + optgroup->append_single_option_line("support_material_bottom_interface_layers", category_path + "interface-layers"); optgroup->append_single_option_line("support_material_interface_pattern", category_path + "interface-pattern"); optgroup->append_single_option_line("support_material_interface_spacing", category_path + "interface-pattern-spacing"); optgroup->append_single_option_line("support_material_interface_contact_loops", category_path + "interface-loops"); From f3f10ff002ba4177c84295b744eb79d4a42f8643 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 10 Mar 2021 09:00:54 +0100 Subject: [PATCH 104/285] Follow up on https://github.com/prusa3d/PrusaSlicer/pull/5219#issuecomment-794515454 Initializing random generator in PlacholderParser from system clock. --- src/libslic3r/GCode.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 28a68e1715..aef6222a34 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -1129,6 +1130,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu // Prepare the helper object for replacing placeholders in custom G-code and output filename. m_placeholder_parser = print.placeholder_parser(); m_placeholder_parser.update_timestamp(); + m_placeholder_parser_context.rng = std::mt19937(std::chrono::high_resolution_clock::now().time_since_epoch().count()); print.update_object_placeholders(m_placeholder_parser.config_writable(), ".gcode"); // Get optimal tool ordering to minimize tool switches of a multi-exruder print. From 73b88e6ce0fa21a031490f148751114af73d573c Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 10 Mar 2021 12:14:45 +0100 Subject: [PATCH 105/285] Splitting FDM support gap to top / bottom, introducing support_material_bottom_contact_distance Fixing Crash in support generation after fcb714c (repro attached) #6195 --- src/libslic3r/Preset.cpp | 3 +- src/libslic3r/Print.cpp | 3 +- src/libslic3r/PrintConfig.cpp | 37 ++++++- src/libslic3r/PrintConfig.hpp | 2 + src/libslic3r/PrintObject.cpp | 15 +-- src/libslic3r/Slicing.cpp | 4 +- src/libslic3r/Slicing.hpp | 46 ++++---- src/libslic3r/SupportMaterial.cpp | 147 ++++++++++++-------------- src/slic3r/GUI/ConfigManipulation.cpp | 1 + src/slic3r/GUI/Plater.cpp | 3 +- src/slic3r/GUI/Tab.cpp | 1 + 11 files changed, 144 insertions(+), 118 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 412dbc6785..7dac343429 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -429,7 +429,8 @@ const std::vector& Preset::print_options() "raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion", "support_material_pattern", "support_material_with_sheath", "support_material_spacing", "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", "support_material_bottom_interface_layers", - "support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance", + "support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops", + "support_material_contact_distance", "support_material_bottom_contact_distance", "support_material_buildplate_only", "dont_support_bridges", "thick_bridges", "notes", "complete_objects", "extruder_clearance_radius", "extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "perimeter_extruder", "infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 79c4e72a7d..e39fd8685e 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1326,7 +1326,8 @@ std::string Print::validate(std::string* warning) const return L("The Wipe Tower is only supported for multiple objects if they have equal layer heights"); if (slicing_params.raft_layers() != slicing_params0.raft_layers()) return L("The Wipe Tower is only supported for multiple objects if they are printed over an equal number of raft layers"); - if (object->config().support_material_contact_distance != m_objects.front()->config().support_material_contact_distance) + if (slicing_params0.gap_object_support != slicing_params.gap_object_support || + slicing_params0.gap_support_object != slicing_params.gap_support_object) return L("The Wipe Tower is only supported for multiple objects if they are printed with the same support_material_contact_distance"); if (! equal_layering(slicing_params, slicing_params0)) return L("The Wipe Tower is only supported for multiple objects if they are sliced equally."); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index d744cb2a75..5fa4dfb013 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2235,7 +2235,7 @@ void PrintConfigDef::init_fff_params() def = this->add("support_material_contact_distance", coFloat); def->gui_type = "f_enum_open"; - def->label = L("Contact Z distance"); + def->label = L("Top contact Z distance"); def->category = L("Support material"); def->tooltip = L("The vertical distance between object and support material interface. " "Setting this to 0 will also prevent Slic3r from using bridge flow and speed " @@ -2243,12 +2243,31 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm"); // def->min = 0; def->enum_values.push_back("0"); + def->enum_values.push_back("0.1"); def->enum_values.push_back("0.2"); def->enum_labels.push_back(L("0 (soluble)")); + def->enum_labels.push_back(L("0.1 (detachable)")); def->enum_labels.push_back(L("0.2 (detachable)")); def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloat(0.2)); + def = this->add("support_material_bottom_contact_distance", coFloat); + def->gui_type = "f_enum_open"; + def->label = L("Bottom contact Z distance"); + def->category = L("Support material"); + def->tooltip = L("The vertical distance between the object top surface and the support material interface. " + "If set to zero, support_material_contact_distance will be used for both top and bottom contact Z distances."); + def->sidetext = L("mm"); +// def->min = 0; + def->enum_values.push_back("0"); + def->enum_values.push_back("0.1"); + def->enum_values.push_back("0.2"); + def->enum_labels.push_back(L("same as top")); + def->enum_labels.push_back(L("0.1")); + def->enum_labels.push_back(L("0.2")); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0)); + def = this->add("support_material_enforce_layers", coInt); def->label = L("Enforce support for the first"); def->category = L("Support material"); @@ -2298,22 +2317,36 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionInt(1)); - def = this->add("support_material_interface_layers", coInt); + auto support_material_interface_layers = def = this->add("support_material_interface_layers", coInt); + def->gui_type = "f_enum_open"; def->label = L("Top interface layers"); def->category = L("Support material"); def->tooltip = L("Number of interface layers to insert between the object(s) and support material."); def->sidetext = L("layers"); def->min = 0; + def->enum_values.push_back("0"); + def->enum_values.push_back("1"); + def->enum_values.push_back("2"); + def->enum_values.push_back("3"); + def->enum_labels.push_back(L("0 (off)")); + def->enum_labels.push_back(L("1 (light)")); + def->enum_labels.push_back(L("2 (default)")); + def->enum_labels.push_back(L("3 (heavy)")); def->mode = comAdvanced; def->set_default_value(new ConfigOptionInt(3)); def = this->add("support_material_bottom_interface_layers", coInt); + def->gui_type = "f_enum_open"; def->label = L("Bottom interface layers"); def->category = L("Support material"); def->tooltip = L("Number of interface layers to insert between the object(s) and support material. " "Set to -1 to use support_material_interface_layers"); def->sidetext = L("layers"); def->min = -1; + def->enum_values.push_back("-1"); + append(def->enum_values, support_material_interface_layers->enum_values); + def->enum_labels.push_back(L("same as top")); + append(def->enum_labels, support_material_interface_layers->enum_labels); def->mode = comAdvanced; def->set_default_value(new ConfigOptionInt(-1)); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 4a4647b736..abeb29d4b8 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -503,6 +503,7 @@ public: ConfigOptionFloat support_material_angle; ConfigOptionBool support_material_buildplate_only; ConfigOptionFloat support_material_contact_distance; + ConfigOptionFloat support_material_bottom_contact_distance; ConfigOptionInt support_material_enforce_layers; ConfigOptionInt support_material_extruder; ConfigOptionFloatOrPercent support_material_extrusion_width; @@ -555,6 +556,7 @@ protected: OPT_PTR(support_material_angle); OPT_PTR(support_material_buildplate_only); OPT_PTR(support_material_contact_distance); + OPT_PTR(support_material_bottom_contact_distance); OPT_PTR(support_material_enforce_layers); OPT_PTR(support_material_interface_contact_loops); OPT_PTR(support_material_extruder); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index a3b0fb787d..e86eb2c9ce 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -546,15 +546,9 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "extra_perimeters" || opt_key == "gap_fill_enabled" || opt_key == "gap_fill_speed" - || opt_key == "overhangs" || opt_key == "first_layer_extrusion_width" - || opt_key == "fuzzy_skin" - || opt_key == "fuzzy_skin_thickness" - || opt_key == "fuzzy_skin_point_dist" || opt_key == "perimeter_extrusion_width" || opt_key == "infill_overlap" - || opt_key == "thin_walls" - || opt_key == "thick_bridges" || opt_key == "external_perimeters_first") { steps.emplace_back(posPerimeters); } else if ( @@ -586,6 +580,7 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "support_material_enforce_layers" || opt_key == "support_material_extruder" || opt_key == "support_material_extrusion_width" + || opt_key == "support_material_bottom_contact_distance" || opt_key == "support_material_interface_layers" || opt_key == "support_material_bottom_interface_layers" || opt_key == "support_material_interface_pattern" @@ -654,7 +649,13 @@ bool PrintObject::invalidate_state_by_config_options( steps.emplace_back(posPrepareInfill); } else if ( opt_key == "external_perimeter_extrusion_width" - || opt_key == "perimeter_extruder") { + || opt_key == "perimeter_extruder" + || opt_key == "fuzzy_skin" + || opt_key == "fuzzy_skin_thickness" + || opt_key == "fuzzy_skin_point_dist" + || opt_key == "overhangs" + || opt_key == "thin_walls" + || opt_key == "thick_bridges") { steps.emplace_back(posPerimeters); steps.emplace_back(posSupportMaterial); } else if (opt_key == "bridge_flow_ratio") { diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index 083b41202d..d0b1e9ce26 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -112,8 +112,10 @@ SlicingParameters SlicingParameters::create_from_config( if (! soluble_interface) { params.gap_raft_object = object_config.raft_contact_distance.value; - params.gap_object_support = object_config.support_material_contact_distance.value; + params.gap_object_support = object_config.support_material_bottom_contact_distance.value; params.gap_support_object = object_config.support_material_contact_distance.value; + if (params.gap_object_support <= 0) + params.gap_object_support = params.gap_support_object; } if (params.base_raft_layers > 0) { diff --git a/src/libslic3r/Slicing.hpp b/src/libslic3r/Slicing.hpp index 8b3d5c917e..489b2768fb 100644 --- a/src/libslic3r/Slicing.hpp +++ b/src/libslic3r/Slicing.hpp @@ -26,7 +26,7 @@ class DynamicPrintConfig; // (using a normal flow over a soluble support, using a bridging flow over a non-soluble support). struct SlicingParameters { - SlicingParameters() { memset(this, 0, sizeof(SlicingParameters)); } + SlicingParameters() = default; static SlicingParameters create_from_config( const PrintConfig &print_config, @@ -44,58 +44,58 @@ struct SlicingParameters // Height of the object to be printed. This value does not contain the raft height. coordf_t object_print_z_height() const { return object_print_z_max - object_print_z_min; } - bool valid; + bool valid { false }; // Number of raft layers. - size_t base_raft_layers; + size_t base_raft_layers { 0 }; // Number of interface layers including the contact layer. - size_t interface_raft_layers; + size_t interface_raft_layers { 0 }; // Layer heights of the raft (base, interface and a contact layer). - coordf_t base_raft_layer_height; - coordf_t interface_raft_layer_height; - coordf_t contact_raft_layer_height; + coordf_t base_raft_layer_height { 0 }; + coordf_t interface_raft_layer_height { 0 }; + coordf_t contact_raft_layer_height { 0 }; // The regular layer height, applied for all but the first layer, if not overridden by layer ranges // or by the variable layer thickness table. - coordf_t layer_height; + coordf_t layer_height { 0 }; // Minimum / maximum layer height, to be used for the automatic adaptive layer height algorithm, // or by an interactive layer height editor. - coordf_t min_layer_height; - coordf_t max_layer_height; - coordf_t max_suport_layer_height; + coordf_t min_layer_height { 0 }; + coordf_t max_layer_height { 0 }; + coordf_t max_suport_layer_height { 0 }; // First layer height of the print, this may be used for the first layer of the raft // or for the first layer of the print. - coordf_t first_print_layer_height; + coordf_t first_print_layer_height { 0 }; // Thickness of the first layer. This is either the first print layer thickness if printed without a raft, // or a bridging flow thickness if printed over a non-soluble raft, // or a normal layer height if printed over a soluble raft. - coordf_t first_object_layer_height; + coordf_t first_object_layer_height { 0 }; // If the object is printed over a non-soluble raft, the first layer may be printed with a briding flow. - bool first_object_layer_bridging; + bool first_object_layer_bridging { false }; // Soluble interface? (PLA soluble in water, HIPS soluble in lemonen) // otherwise the interface must be broken off. - bool soluble_interface; + bool soluble_interface { false }; // Gap when placing object over raft. - coordf_t gap_raft_object; + coordf_t gap_raft_object { 0 }; // Gap when placing support over object. - coordf_t gap_object_support; + coordf_t gap_object_support { 0 }; // Gap when placing object over support. - coordf_t gap_support_object; + coordf_t gap_support_object { 0 }; // Bottom and top of the printed object. // If printed without a raft, object_print_z_min = 0 and object_print_z_max = object height. // Otherwise object_print_z_min is equal to the raft height. - coordf_t raft_base_top_z; - coordf_t raft_interface_top_z; - coordf_t raft_contact_top_z; + coordf_t raft_base_top_z { 0 }; + coordf_t raft_interface_top_z { 0 }; + coordf_t raft_contact_top_z { 0 }; // In case of a soluble interface, object_print_z_min == raft_contact_top_z, otherwise there is a gap between the raft and the 1st object layer. - coordf_t object_print_z_min; - coordf_t object_print_z_max; + coordf_t object_print_z_min { 0 }; + coordf_t object_print_z_max { 0 }; }; static_assert(IsTriviallyCopyable::value, "SlicingParameters class is not POD (and it should be - see constructor)."); diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 7747559e63..8dc939aed1 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -397,14 +398,6 @@ inline void layers_append(PrintObjectSupportMaterial::MyLayersPtr &dst, const Pr dst.insert(dst.end(), src.begin(), src.end()); } -// Compare layers lexicographically. -struct MyLayersPtrCompare -{ - bool operator()(const PrintObjectSupportMaterial::MyLayer* layer1, const PrintObjectSupportMaterial::MyLayer* layer2) const { - return *layer1 < *layer2; - } -}; - void PrintObjectSupportMaterial::generate(PrintObject &object) { BOOST_LOG_TRIVIAL(info) << "Support generator - Start"; @@ -467,10 +460,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) MyLayersPtr intermediate_layers = this->raft_and_intermediate_support_layers( object, bottom_contacts, top_contacts, layer_storage); -// this->trim_support_layers_by_object(object, top_contacts, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, 0., m_gap_xy); - this->trim_support_layers_by_object(object, top_contacts, - m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, - m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, m_gap_xy); + this->trim_support_layers_by_object(object, top_contacts, m_slicing_params.gap_support_object, m_slicing_params.gap_object_support, m_gap_xy); #ifdef SLIC3R_DEBUG for (const MyLayer *layer : top_contacts) @@ -552,7 +542,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) layers_append(layers_sorted, interface_layers); layers_append(layers_sorted, base_interface_layers); // Sort the layers lexicographically by a raising print_z and a decreasing height. - std::sort(layers_sorted.begin(), layers_sorted.end(), MyLayersPtrCompare()); + std::sort(layers_sorted.begin(), layers_sorted.end(), [](auto *l1, auto *l2) { return *l1 < *l2; }); int layer_id = 0; assert(object.support_layers().empty()); for (size_t i = 0; i < layers_sorted.size();) { @@ -1585,11 +1575,11 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ } else if (m_slicing_params.soluble_interface) { // Align the contact surface height with a layer immediately below the supported layer. // Interface layer will be synchronized with the object. - new_layer.print_z = layer.print_z - layer.height; + new_layer.print_z = layer.bottom_z(); new_layer.height = object.layers()[layer_id - 1]->height; new_layer.bottom_z = (layer_id == 1) ? m_slicing_params.object_print_z_min : object.layers()[layer_id - 2]->print_z; } else { - new_layer.print_z = layer.print_z - layer.height - m_object_config->support_material_contact_distance; + new_layer.print_z = layer.bottom_z() - m_slicing_params.gap_object_support; new_layer.bottom_z = new_layer.print_z; new_layer.height = 0.; // Ignore this contact area if it's too low. @@ -1616,7 +1606,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ for (const LayerRegion *region : layer.regions()) bridging_height += region->region()->bridging_height_avg(*m_print_config); bridging_height /= coordf_t(layer.regions().size()); - coordf_t bridging_print_z = layer.print_z - bridging_height - m_object_config->support_material_contact_distance; + coordf_t bridging_print_z = layer.print_z - bridging_height - m_slicing_params.gap_support_object; if (bridging_print_z >= m_slicing_params.first_print_layer_height - EPSILON) { // Not below the first layer height means this layer is printable. if (new_layer.print_z < m_slicing_params.first_print_layer_height + EPSILON) { @@ -1892,7 +1882,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta // Place a bridge flow interface layer or the normal flow interface layer over the top surface. m_support_material_bottom_interface_flow.height(); layer_new.print_z = m_slicing_params.soluble_interface ? object.layers()[layer_id + 1]->print_z : - layer.print_z + layer_new.height + m_object_config->support_material_contact_distance.value; + layer.print_z + layer_new.height + m_slicing_params.gap_object_support; layer_new.bottom_z = layer.print_z; layer_new.idx_object_layer_below = layer_id; layer_new.bridging = ! m_slicing_params.soluble_interface && m_object_config->thick_bridges; @@ -2039,11 +2029,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta task_group.wait(); } std::reverse(bottom_contacts.begin(), bottom_contacts.end()); -// trim_support_layers_by_object(object, bottom_contacts, 0., 0., m_gap_xy); - trim_support_layers_by_object(object, bottom_contacts, - m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, - m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, m_gap_xy); - + trim_support_layers_by_object(object, bottom_contacts, m_slicing_params.gap_support_object, m_slicing_params.gap_object_support, m_gap_xy); } // ! top_contacts.empty() return bottom_contacts; @@ -2473,10 +2459,7 @@ void PrintObjectSupportMaterial::generate_base_layers( ++ iRun; #endif /* SLIC3R_DEBUG */ -// trim_support_layers_by_object(object, intermediate_layers, 0., 0., m_gap_xy); - this->trim_support_layers_by_object(object, intermediate_layers, - m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, - m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, m_gap_xy); + this->trim_support_layers_by_object(object, intermediate_layers, m_slicing_params.gap_support_object, m_slicing_params.gap_object_support, m_gap_xy); } void PrintObjectSupportMaterial::trim_support_layers_by_object( @@ -2510,7 +2493,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object( // BOOST_LOG_TRIVIAL(trace) << "Support generator - trim_support_layers_by_object - trimmming non-empty layer " << idx_layer << " of " << nonempty_layers.size(); assert(! support_layer.polygons.empty() && support_layer.print_z >= m_slicing_params.raft_contact_top_z + EPSILON); // Find the overlapping object layers including the extra above / below gap. - coordf_t z_threshold = support_layer.print_z - support_layer.height - gap_extra_below + EPSILON; + coordf_t z_threshold = support_layer.bottom_print_z() - gap_extra_below + EPSILON; idx_object_layer_overlapping = idx_higher_or_equal( object.layers().begin(), object.layers().end(), idx_object_layer_overlapping, [z_threshold](const Layer *layer){ return layer->print_z >= z_threshold; }); @@ -2519,7 +2502,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object( size_t i = idx_object_layer_overlapping; for (; i < object.layers().size(); ++ i) { const Layer &object_layer = *object.layers()[i]; - if (object_layer.print_z - object_layer.height > support_layer.print_z + gap_extra_above - EPSILON) + if (object_layer.bottom_z() > support_layer.print_z + gap_extra_above - EPSILON) break; polygons_append(polygons_trimming, offset(object_layer.lslices, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS)); } @@ -2537,6 +2520,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object( offset(to_expolygons(region->fill_surfaces.filter_by_type(stBottomBridge)), gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS)); if (region->region()->config().overhangs.value) + // Add bridging perimeters. SupportMaterialInternal::collect_bridging_perimeter_areas(region->perimeters, gap_xy_scaled, polygons_trimming); } if (! some_region_overlaps) @@ -2896,7 +2880,8 @@ static inline void fill_expolygons_with_sheath_generate_paths( float density, ExtrusionRole role, const Flow &flow, - bool with_sheath) + bool with_sheath, + bool no_sort) { if (polygons.empty()) return; @@ -2916,8 +2901,12 @@ static inline void fill_expolygons_with_sheath_generate_paths( for (ExPolygon &expoly : offset2_ex(polygons, float(SCALED_EPSILON), float(- SCALED_EPSILON - 0.5*flow.scaled_width()))) { // Don't reorder the skirt and its infills. - auto eec = std::make_unique(); - eec->no_sort = true; + std::unique_ptr eec; + if (no_sort) { + eec = std::make_unique(); + eec->no_sort = true; + } + ExtrusionEntitiesPtr &out = no_sort ? eec->entities : dst; // Draw the perimeters. Polylines polylines; polylines.reserve(expoly.holes.size() + 1); @@ -2927,10 +2916,11 @@ static inline void fill_expolygons_with_sheath_generate_paths( pl.clip_end(clip_length); polylines.emplace_back(std::move(pl)); } - extrusion_entities_append_paths(eec->entities, polylines, erSupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height()); + extrusion_entities_append_paths(out, polylines, erSupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height()); // Fill in the rest. - fill_expolygons_generate_paths(eec->entities, offset_ex(expoly, float(-0.4 * spacing)), filler, fill_params, density, role, flow); - dst.emplace_back(eec.release()); + fill_expolygons_generate_paths(out, offset_ex(expoly, float(-0.4 * spacing)), filler, fill_params, density, role, flow); + if (no_sort) + dst.emplace_back(eec.release()); } } @@ -3255,6 +3245,9 @@ static std::string dbg_index_to_color(int idx) // Therefore the bottom interface spots are expanded a bit. The expanded regions may overlap with another bottom interface layers, // leading to over extrusion, where they overlap. The over extrusion is better avoided as it often makes the interface layers // to stick too firmly to the object. +// +// Modulate thickness (increase bottom_z) of extrusions_in_out generated for this_layer +// if they overlap with overlapping_layers, whose print_z is above this_layer.bottom_z() and below this_layer.print_z. void modulate_extrusion_by_overlapping_layers( // Extrusions generated for this_layer. ExtrusionEntitiesPtr &extrusions_in_out, @@ -3343,8 +3336,8 @@ void modulate_extrusion_by_overlapping_layers( // Collect the paths of this_layer. { Polylines &polylines = path_fragments.back().polylines; - for (ExtrusionEntitiesPtr::const_iterator it = extrusions_in_out.begin(); it != extrusions_in_out.end(); ++ it) { - ExtrusionPath *path = dynamic_cast(*it); + for (ExtrusionEntity *ee : extrusions_in_out) { + ExtrusionPath *path = dynamic_cast(ee); assert(path != nullptr); polylines.emplace_back(Polyline(std::move(path->polyline))); path_ends.emplace_back(std::pair(polylines.back().points.front(), polylines.back().points.back())); @@ -3504,7 +3497,6 @@ void PrintObjectSupportMaterial::generate_toolpaths( const MyLayersPtr &interface_layers, const MyLayersPtr &base_interface_layers) const { -// Slic3r::debugf "Generating patterns\n"; // loop_interface_processor with a given circle radius. LoopInterfaceProcessor loop_interface_processor(1.5 * m_support_material_interface_flow.scaled_width()); loop_interface_processor.n_contact_loops = this->has_contact_loops() ? 1 : 0; @@ -3603,7 +3595,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( filler, float(support_density), // Extrusion parameters erSupportMaterial, flow, - with_sheath); + with_sheath, false); } } @@ -3636,7 +3628,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( // Extrusion parameters (support_layer_id < m_slicing_params.base_raft_layers) ? erSupportMaterial : erSupportMaterialInterface, flow, // sheath at first layer - support_layer_id == 0); + support_layer_id == 0, support_layer_id == 0); } }); @@ -3646,12 +3638,20 @@ void PrintObjectSupportMaterial::generate_toolpaths( std::vector overlapping; }; struct LayerCache { - MyLayerExtruded bottom_contact_layer; - MyLayerExtruded top_contact_layer; - MyLayerExtruded base_layer; - MyLayerExtruded interface_layer; - MyLayerExtruded base_interface_layer; - std::vector overlaps; + MyLayerExtruded bottom_contact_layer; + MyLayerExtruded top_contact_layer; + MyLayerExtruded base_layer; + MyLayerExtruded interface_layer; + MyLayerExtruded base_interface_layer; + boost::container::static_vector nonempty; + + void add_nonempty_and_sort() { + for (MyLayerExtruded *item : { &bottom_contact_layer, &top_contact_layer, &interface_layer, &base_interface_layer, &base_layer }) + if (! item->empty()) + this->nonempty.emplace_back(item); + // Sort the layers with the same print_z coordinate by their heights, thickest first. + std::stable_sort(this->nonempty.begin(), this->nonempty.end(), [](const LayerCacheItem &lc1, const LayerCacheItem &lc2) { return lc1.layer_extruded->layer->height > lc2.layer_extruded->layer->height; }); + } }; std::vector layer_caches(support_layers.size(), LayerCache()); @@ -3816,6 +3816,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_density)); float density = float(support_density); bool sheath = with_sheath; + bool no_sort = false; if (base_layer.layer->bottom_z < EPSILON) { // Base flange (the 1st layer). filler = filler_first_layer; @@ -3827,7 +3828,8 @@ void PrintObjectSupportMaterial::generate_toolpaths( //FIXME When paralellizing, each thread shall have its own copy of the fillers. filler->spacing = flow.spacing(); filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density)); - sheath = true; + sheath = true; + no_sort = true; } fill_expolygons_with_sheath_generate_paths( // Destination @@ -3838,7 +3840,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( filler, density, // Extrusion parameters erSupportMaterial, flow, - sheath); + sheath, no_sort); } @@ -3847,24 +3849,13 @@ void PrintObjectSupportMaterial::generate_toolpaths( base_layer.could_merge(base_interface_layer)) base_layer.merge(std::move(base_interface_layer)); - layer_cache.overlaps.reserve(5); - if (! bottom_contact_layer.empty()) - layer_cache.overlaps.push_back(&bottom_contact_layer); - if (! top_contact_layer.empty()) - layer_cache.overlaps.push_back(&top_contact_layer); - if (! interface_layer.empty()) - layer_cache.overlaps.push_back(&interface_layer); - if (! base_interface_layer.empty()) - layer_cache.overlaps.push_back(&base_interface_layer); - if (! base_layer.empty()) - layer_cache.overlaps.push_back(&base_layer); - // Sort the layers with the same print_z coordinate by their heights, thickest first. - std::sort(layer_cache.overlaps.begin(), layer_cache.overlaps.end(), [](const LayerCacheItem &lc1, const LayerCacheItem &lc2) { return lc1.layer_extruded->layer->height > lc2.layer_extruded->layer->height; }); + layer_cache.add_nonempty_and_sort(); + // Collect the support areas with this print_z into islands, as there is no need // for retraction over these islands. Polygons polys; // Collect the extrusions, sorted by the bottom extrusion height. - for (LayerCacheItem &layer_cache_item : layer_cache.overlaps) { + for (LayerCacheItem &layer_cache_item : layer_cache.nonempty) { // Collect islands to polys. layer_cache_item.layer_extruded->polygons_append(polys); // The print_z of the top contact surfaces and bottom_z of the bottom contact surfaces are "free" @@ -3878,32 +3869,22 @@ void PrintObjectSupportMaterial::generate_toolpaths( // Collect overlapping top/bottom surfaces. layer_cache_item.overlapping.reserve(20); coordf_t bottom_z = layer_cache_item.layer_extruded->layer->bottom_print_z() + EPSILON; - for (int i = int(idx_layer_bottom_contact) - 1; i >= 0 && bottom_contacts[i]->print_z > bottom_z; -- i) - layer_cache_item.overlapping.push_back(bottom_contacts[i]); - for (int i = int(idx_layer_top_contact) - 1; i >= 0 && top_contacts[i]->print_z > bottom_z; -- i) - layer_cache_item.overlapping.push_back(top_contacts[i]); + auto add_overlapping = [&layer_cache_item, bottom_z](const MyLayersPtr &layers, size_t idx_top) { + for (int i = int(idx_top) - 1; i >= 0 && layers[i]->print_z > bottom_z; -- i) + layer_cache_item.overlapping.push_back(layers[i]); + }; + add_overlapping(top_contacts, idx_layer_top_contact); if (layer_cache_item.layer_extruded->layer->layer_type == sltBottomContact) { // Bottom contact layer may overlap with a base layer, which may be changed to interface layer. - for (int i = int(idx_layer_intermediate) - 1; i >= 0 && intermediate_layers[i]->print_z > bottom_z; -- i) - layer_cache_item.overlapping.push_back(intermediate_layers[i]); - for (int i = int(idx_layer_interface) - 1; i >= 0 && interface_layers[i]->print_z > bottom_z; -- i) - layer_cache_item.overlapping.push_back(interface_layers[i]); - for (int i = int(idx_layer_base_interface) - 1; i >= 0 && base_interface_layers[i]->print_z > bottom_z; -- i) - layer_cache_item.overlapping.push_back(base_interface_layers[i]); + add_overlapping(intermediate_layers, idx_layer_intermediate); + add_overlapping(interface_layers, idx_layer_interface); + add_overlapping(base_interface_layers, idx_layer_base_interface); } - std::sort(layer_cache_item.overlapping.begin(), layer_cache_item.overlapping.end(), MyLayersPtrCompare()); + // Order the layers by lexicographically by an increasing print_z and a decreasing layer height. + std::stable_sort(layer_cache_item.overlapping.begin(), layer_cache_item.overlapping.end(), [](auto *l1, auto *l2) { return *l1 < *l2; }); } if (! polys.empty()) expolygons_append(support_layer.support_islands.expolygons, union_ex(polys)); - /* { - require "Slic3r/SVG.pm"; - Slic3r::SVG::output("islands_" . $z . ".svg", - red_expolygons => union_ex($contact), - green_expolygons => union_ex($interface), - green_polylines => [ map $_->unpack->polyline, @{$layer->support_contact_fills} ], - polylines => [ map $_->unpack->polyline, @{$layer->support_fills} ], - ); - } */ } // for each support_layer_id }); @@ -3914,7 +3895,9 @@ void PrintObjectSupportMaterial::generate_toolpaths( for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) { SupportLayer &support_layer = *support_layers[support_layer_id]; LayerCache &layer_cache = layer_caches[support_layer_id]; - for (LayerCacheItem &layer_cache_item : layer_cache.overlaps) { + // For all extrusion types at this print_z, ordered by decreasing layer height: + for (LayerCacheItem &layer_cache_item : layer_cache.nonempty) { + // Trim the extrusion height from the bottom by the overlapping layers. modulate_extrusion_by_overlapping_layers(layer_cache_item.layer_extruded->extrusions, *layer_cache_item.layer_extruded->layer, layer_cache_item.overlapping); support_layer.support_fills.append(std::move(layer_cache_item.layer_extruded->extrusions)); } diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 80ebe6ea80..6e0f135284 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -285,6 +285,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) "support_material_xy_spacing" }) toggle_field(el, have_support_material); toggle_field("support_material_threshold", have_support_material_auto); + toggle_field("support_material_bottom_contact_distance", have_support_material && ! have_support_soluble); for (auto el : { "support_material_bottom_interface_layers", "support_material_interface_spacing", "support_material_interface_extruder", "support_material_interface_speed", "support_material_interface_contact_loops" }) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index c6b3ed89f8..65b4b7b283 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1762,7 +1762,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) // These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor. "layer_height", "first_layer_height", "min_layer_height", "max_layer_height", "brim_width", "perimeters", "perimeter_extruder", "fill_density", "infill_extruder", "top_solid_layers", - "support_material", "support_material_extruder", "support_material_interface_extruder", "support_material_contact_distance", "raft_layers" + "support_material", "support_material_extruder", "support_material_interface_extruder", + "support_material_contact_distance", "support_material_bottom_contact_distance", "raft_layers" })) , sidebar(new Sidebar(q)) , m_ui_jobs(this) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 472db49761..7b3f23eb46 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1507,6 +1507,7 @@ void TabPrint::build() optgroup = page->new_optgroup(L("Options for support material and raft")); optgroup->append_single_option_line("support_material_contact_distance", category_path + "contact-z-distance"); + optgroup->append_single_option_line("support_material_bottom_contact_distance", category_path + "contact-z-distance"); optgroup->append_single_option_line("support_material_pattern", category_path + "pattern"); optgroup->append_single_option_line("support_material_with_sheath", category_path + "with-sheath-around-the-support"); optgroup->append_single_option_line("support_material_spacing", category_path + "pattern-spacing-0-inf"); From 2494a8f384ff12f40e3423722f74d3864fe2ed57 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 10 Mar 2021 16:16:00 +0100 Subject: [PATCH 106/285] Allowing ints with open enums in combo boxes. --- src/libslic3r/PrintConfig.cpp | 4 ++-- src/slic3r/GUI/Field.cpp | 20 ++++++++++++++++---- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 5fa4dfb013..3aad02f91a 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2318,7 +2318,7 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionInt(1)); auto support_material_interface_layers = def = this->add("support_material_interface_layers", coInt); - def->gui_type = "f_enum_open"; + def->gui_type = "i_enum_open"; def->label = L("Top interface layers"); def->category = L("Support material"); def->tooltip = L("Number of interface layers to insert between the object(s) and support material."); @@ -2336,7 +2336,7 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionInt(3)); def = this->add("support_material_bottom_interface_layers", coInt); - def->gui_type = "f_enum_open"; + def->gui_type = "i_enum_open"; def->label = L("Bottom interface layers"); def->category = L("Support material"); def->tooltip = L("Number of interface layers to insert between the object(s) and support material. " diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 7303f4d72d..02ef877759 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -965,15 +965,27 @@ void Choice::BUILD() { } if (is_defined_input_value(window, m_opt.type)) { - if (m_opt.type == coFloatOrPercent) { + switch (m_opt.type) { + case coFloatOrPercent: + { std::string old_val = !m_value.empty() ? boost::any_cast(m_value) : ""; if (old_val == boost::any_cast(get_value())) return; + break; } - else { - double old_val = !m_value.empty() ? boost::any_cast(m_value) : -99999; - if (fabs(old_val - boost::any_cast(get_value())) <= 0.0001) + case coInt: + { + int old_val = !m_value.empty() ? boost::any_cast(m_value) : 0; + if (old_val == boost::any_cast(get_value())) return; + break; + } + default: + { + double old_val = !m_value.empty() ? boost::any_cast(m_value) : -99999; + if (fabs(old_val - boost::any_cast(get_value())) <= 0.0001) + return; + } } on_change_field(); } From 9c80c6a4af809c009c98225d8a79472b4dad73d2 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 11 Mar 2021 11:48:27 +0100 Subject: [PATCH 107/285] Disable multi-sampling with virgl (VirtualGL) on Linux. Namely, on ChromeOS virgl flips red/blue channels at least on some computers with multi-sampling enabled. It seems it is sufficient to disable multi-sampling after the OpenGL context is created. --- src/slic3r/GUI/OpenGLManager.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/slic3r/GUI/OpenGLManager.cpp b/src/slic3r/GUI/OpenGLManager.cpp index 0fa97bfc11..693f544e7a 100644 --- a/src/slic3r/GUI/OpenGLManager.cpp +++ b/src/slic3r/GUI/OpenGLManager.cpp @@ -256,6 +256,11 @@ bool OpenGLManager::init_gl() } if (valid_version) { + if (s_gl_info.get_renderer() == "virgl") + // Disable multi-sampling with virgl (VirtualGL) on Linux. + // Namely, on ChromeOS virgl flips red/blue channels at least on some computers with multi-sampling enabled. + // It seems it is sufficient to disable multi-sampling after the OpenGL context is created. + s_multisample = EMultisampleState::Disabled; // load shaders auto [result, error] = m_shaders_manager.init(); if (!result) { From 2b1970872078166f24289342594a16d5c0f8bac8 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 11 Mar 2021 12:22:06 +0100 Subject: [PATCH 108/285] Further ChromeOS support: Detect removable media mounted through ChromeOS --- src/slic3r/GUI/OpenGLManager.cpp | 2 ++ src/slic3r/GUI/RemovableDriveManager.cpp | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/slic3r/GUI/OpenGLManager.cpp b/src/slic3r/GUI/OpenGLManager.cpp index 693f544e7a..9a1eecc111 100644 --- a/src/slic3r/GUI/OpenGLManager.cpp +++ b/src/slic3r/GUI/OpenGLManager.cpp @@ -256,11 +256,13 @@ bool OpenGLManager::init_gl() } if (valid_version) { +#ifdef __linux__ if (s_gl_info.get_renderer() == "virgl") // Disable multi-sampling with virgl (VirtualGL) on Linux. // Namely, on ChromeOS virgl flips red/blue channels at least on some computers with multi-sampling enabled. // It seems it is sufficient to disable multi-sampling after the OpenGL context is created. s_multisample = EMultisampleState::Disabled; +#endif // __linux__ // load shaders auto [result, error] = m_shaders_manager.init(); if (!result) { diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp index a1247f3a04..85d91382a0 100644 --- a/src/slic3r/GUI/RemovableDriveManager.cpp +++ b/src/slic3r/GUI/RemovableDriveManager.cpp @@ -247,6 +247,9 @@ std::vector RemovableDriveManager::search_for_removable_drives() cons path = "/run" + path; pp = "/run"+pp; search_for_drives_internal::search_path(path, pp, current_drives); + + // ChromeOS specific: search /mnt/chromeos/removable/* folder + search_for_drives_internal::search_path("/mnt/chromeos/removable/*", "/mnt/chromeos/removable", current_drives); #endif return current_drives; From ca8cf0a9f12b8f2046ddd1cfdd0a06f45cb05f5e Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 11 Mar 2021 13:25:13 +0100 Subject: [PATCH 109/285] Detecting platform, namely reading /proc/version to detect Chromium OS. Disabling Eject on Chromium, detecting removable media the Chromium way. --- src/slic3r/CMakeLists.txt | 2 ++ src/slic3r/GUI/GUI_Init.cpp | 3 ++ src/slic3r/GUI/OpenGLManager.cpp | 16 +++++----- src/slic3r/GUI/Plater.cpp | 5 ++- src/slic3r/GUI/RemovableDriveManager.cpp | 39 ++++++++++++++---------- 5 files changed, 40 insertions(+), 25 deletions(-) diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 505c8f0f21..40d74159dd 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -207,6 +207,8 @@ set(SLIC3R_GUI_SOURCES Utils/Bonjour.hpp Utils/PresetUpdater.cpp Utils/PresetUpdater.hpp + Utils/Platform.cpp + Utils/Platform.hpp Utils/Process.cpp Utils/Process.hpp Utils/Profile.hpp diff --git a/src/slic3r/GUI/GUI_Init.cpp b/src/slic3r/GUI/GUI_Init.cpp index 839782741f..3c77f33358 100644 --- a/src/slic3r/GUI/GUI_Init.cpp +++ b/src/slic3r/GUI/GUI_Init.cpp @@ -9,6 +9,7 @@ #include "slic3r/GUI/format.hpp" #include "slic3r/GUI/MainFrame.hpp" #include "slic3r/GUI/Plater.hpp" +#include "slic3r/Utils/Platform.hpp" // To show a message box if GUI initialization ends up with an exception thrown. #include @@ -36,6 +37,8 @@ int GUI_Run(GUI_InitParams ¶ms) signal(SIGCHLD, SIG_DFL); #endif // __APPLE__ + detect_platform(); + try { GUI::GUI_App* gui = new GUI::GUI_App(params.start_as_gcodeviewer ? GUI::GUI_App::EAppMode::GCodeViewer : GUI::GUI_App::EAppMode::Editor); if (gui->get_app_mode() != GUI::GUI_App::EAppMode::GCodeViewer) { diff --git a/src/slic3r/GUI/OpenGLManager.cpp b/src/slic3r/GUI/OpenGLManager.cpp index 9a1eecc111..8e87740360 100644 --- a/src/slic3r/GUI/OpenGLManager.cpp +++ b/src/slic3r/GUI/OpenGLManager.cpp @@ -4,6 +4,7 @@ #include "GUI.hpp" #include "I18N.hpp" #include "3DScene.hpp" +#include "slic3r/Utils/Platform.hpp" #include @@ -256,13 +257,6 @@ bool OpenGLManager::init_gl() } if (valid_version) { -#ifdef __linux__ - if (s_gl_info.get_renderer() == "virgl") - // Disable multi-sampling with virgl (VirtualGL) on Linux. - // Namely, on ChromeOS virgl flips red/blue channels at least on some computers with multi-sampling enabled. - // It seems it is sufficient to disable multi-sampling after the OpenGL context is created. - s_multisample = EMultisampleState::Disabled; -#endif // __linux__ // load shaders auto [result, error] = m_shaders_manager.init(); if (!result) { @@ -326,7 +320,13 @@ void OpenGLManager::detect_multisample(int* attribList) { int wxVersion = wxMAJOR_VERSION * 10000 + wxMINOR_VERSION * 100 + wxRELEASE_NUMBER; bool enable_multisample = wxVersion >= 30003; - s_multisample = (enable_multisample && wxGLCanvas::IsDisplaySupported(attribList)) ? EMultisampleState::Enabled : EMultisampleState::Disabled; + s_multisample = + enable_multisample && + // Disable multi-sampling on ChromeOS, as the OpenGL virtualization swaps Red/Blue channels with multi-sampling enabled, + // at least on some platforms. + (platform() != Platform::Linux || platform_flavor() != PlatformFlavor::LinuxOnChromium) && + wxGLCanvas::IsDisplaySupported(attribList) + ? EMultisampleState::Enabled : EMultisampleState::Disabled; // Alternative method: it was working on previous version of wxWidgets but not with the latest, at least on Windows // s_multisample = enable_multisample && wxGLCanvas::IsExtensionSupported("WGL_ARB_multisample"); } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 65b4b7b283..f131c244fa 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -75,6 +75,7 @@ #include "../Utils/FixModelByWin10.hpp" #include "../Utils/UndoRedo.hpp" #include "../Utils/PresetUpdater.hpp" +#include "../Utils/Platform.hpp" #include "../Utils/Process.hpp" #include "RemovableDriveManager.hpp" #include "InstanceCheck.hpp" @@ -3681,7 +3682,9 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt) // If writing to removable drive was scheduled, show notification with eject button if (exporting_status == ExportingStatus::EXPORTING_TO_REMOVABLE && !has_error) { show_action_buttons(false); - notification_manager->push_exporting_finished_notification(last_output_path, last_output_dir_path, true); + notification_manager->push_exporting_finished_notification(last_output_path, last_output_dir_path, + // Don't offer the "Eject" button on ChromeOS, the Linux side has no control over it. + platform() != Platform::Linux || platform_flavor() != PlatformFlavor::LinuxOnChromium); wxGetApp().removable_drive_manager()->set_exporting_finished(true); }else if (exporting_status == ExportingStatus::EXPORTING_TO_LOCAL && !has_error) notification_manager->push_exporting_finished_notification(last_output_path, last_output_dir_path, false); diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp index 85d91382a0..c200956e19 100644 --- a/src/slic3r/GUI/RemovableDriveManager.cpp +++ b/src/slic3r/GUI/RemovableDriveManager.cpp @@ -1,4 +1,5 @@ #include "RemovableDriveManager.hpp" +#include "slic3r/Utils/Platform.hpp" #include #include @@ -231,25 +232,28 @@ std::vector RemovableDriveManager::search_for_removable_drives() cons #else - //search /media/* folder - search_for_drives_internal::search_path("/media/*", "/media", current_drives); + if (platform() == Platform::Linux && platform_flavor() == PlatformFlavor::LinuxOnChromium) { + // ChromeOS specific: search /mnt/chromeos/removable/* folder + search_for_drives_internal::search_path("/mnt/chromeos/removable/*", "/mnt/chromeos/removable", current_drives); + } else { + //search /media/* folder + search_for_drives_internal::search_path("/media/*", "/media", current_drives); - //search_path("/Volumes/*", "/Volumes"); - std::string path(std::getenv("USER")); - std::string pp(path); + //search_path("/Volumes/*", "/Volumes"); + std::string path(std::getenv("USER")); + std::string pp(path); - //search /media/USERNAME/* folder - pp = "/media/"+pp; - path = "/media/" + path + "/*"; - search_for_drives_internal::search_path(path, pp, current_drives); + //search /media/USERNAME/* folder + pp = "/media/"+pp; + path = "/media/" + path + "/*"; + search_for_drives_internal::search_path(path, pp, current_drives); - //search /run/media/USERNAME/* folder - path = "/run" + path; - pp = "/run"+pp; - search_for_drives_internal::search_path(path, pp, current_drives); + //search /run/media/USERNAME/* folder + path = "/run" + path; + pp = "/run"+pp; + search_for_drives_internal::search_path(path, pp, current_drives); + } - // ChromeOS specific: search /mnt/chromeos/removable/* folder - search_for_drives_internal::search_path("/mnt/chromeos/removable/*", "/mnt/chromeos/removable", current_drives); #endif return current_drives; @@ -446,7 +450,10 @@ RemovableDriveManager::RemovableDrivesStatus RemovableDriveManager::status() RemovableDriveManager::RemovableDrivesStatus out; { tbb::mutex::scoped_lock lock(m_drives_mutex); - out.has_eject = this->find_last_save_path_drive_data() != m_current_drives.end(); + out.has_eject = + // Cannot control eject on Chromium. + (platform() != Platform::Linux || platform_flavor() != PlatformFlavor::LinuxOnChromium) && + this->find_last_save_path_drive_data() != m_current_drives.end(); out.has_removable_drives = ! m_current_drives.empty(); } if (! out.has_eject) From 051ba0e6f4fce9456e6935086285a1cd718b7078 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 11 Mar 2021 13:45:45 +0100 Subject: [PATCH 110/285] New files missing from the previous commit. --- src/slic3r/Utils/Platform.cpp | 59 +++++++++++++++++++++++++++++++++++ src/slic3r/Utils/Platform.hpp | 40 ++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 src/slic3r/Utils/Platform.cpp create mode 100644 src/slic3r/Utils/Platform.hpp diff --git a/src/slic3r/Utils/Platform.cpp b/src/slic3r/Utils/Platform.cpp new file mode 100644 index 0000000000..d30f30fc86 --- /dev/null +++ b/src/slic3r/Utils/Platform.cpp @@ -0,0 +1,59 @@ +#include "Platform.hpp" + + +// For starting another PrusaSlicer instance on OSX. +// Fails to compile on Windows on the build server. + +#include + +namespace Slic3r { +namespace GUI { + +static auto s_platform = Platform::Uninitialized; +static auto s_platform_flavor = PlatformFlavor::Uninitialized; + +void detect_platform() +{ +#if defined(_WIN32) + s_platform = Platform::Windows; + s_platform_flavor = PlatformFlavor::Generic; +#elif defined(__APPLE__) + s_platform = Platform::OSX; + s_platform_flavor = PlatformFlavor::Generic; +#elif defined(__linux__) + s_platform = Platform::Linux; + s_platform_flavor = PlatformFlavor::GenericLinux; + // Test for Chromium. + { + FILE *f = ::fopen("/proc/version", "rt"); + if (f) { + char buf[4096]; + // Read the 1st line. + if (::fgets(buf, 4096, f) && strstr(buf, "Chromium OS") != nullptr) + s_platform_flavor = LinuxOnChromium; + ::fclose(f); + } + } +#elif defined(__OpenBSD__) + s_platform = Platform::BSD; + s_platform_flavor = PlatformFlavor::OpenBSD; +#else + // This should not happen. + static_assert(false, "Unknown platform detected"); + s_platform = Platform::Unknown; + s_platform_flavor = PlatformFlavor::Unknown; +#endif +} + +Platform platform() +{ + return s_platform; +} + +PlatformFlavor platform_flavor() +{ + return s_platform_flavor; +} + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/Utils/Platform.hpp b/src/slic3r/Utils/Platform.hpp new file mode 100644 index 0000000000..e11bde4124 --- /dev/null +++ b/src/slic3r/Utils/Platform.hpp @@ -0,0 +1,40 @@ +#ifndef SLIC3R_GUI_Utils_Platform_HPP +#define SLIC3R_GUI_Utils_Platform_HPP + +namespace Slic3r { +namespace GUI { + +enum class Platform +{ + Uninitialized, + Unknown, + Windows, + OSX, + Linux, + BSD, +}; + +enum class PlatformFlavor +{ + Uninitialized, + Unknown, + // For Windows and OSX, until we need to be more specific. + Generic, + // For Platform::Linux + GenericLinux, + LinuxOnChromium, + // For Platform::BSD + OpenBSD, +}; + +// To be called on program start-up. +void detect_platform(); + +Platform platform(); +PlatformFlavor platform_flavor(); + + +} // namespace GUI +} // namespace Slic3r + +#endif // SLIC3R_GUI_Utils_Platform_HPP From a9c3d270e6384098cb26c6263093e6ead1109a29 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 11 Mar 2021 15:01:24 +0100 Subject: [PATCH 111/285] ConfigOptions: GUI type as enum, not string. Fixing compilation error in the new Platform code. Fixing one issue in FDM support after splitting the top/bottom interface layers. --- src/libslic3r/Config.hpp | 20 +++++++++++++++++++- src/libslic3r/PrintConfig.cpp | 30 +++++++++++++++--------------- src/libslic3r/SupportMaterial.cpp | 4 ++-- src/slic3r/GUI/Field.cpp | 6 ++++-- src/slic3r/GUI/OG_CustomCtrl.cpp | 8 ++++---- src/slic3r/GUI/OptionsGroup.cpp | 30 +++++++++++++++++------------- src/slic3r/GUI/Plater.cpp | 6 +++--- src/slic3r/GUI/Tab.cpp | 2 +- src/slic3r/Utils/Platform.cpp | 2 +- xs/xsp/Config.xsp | 13 ------------- 10 files changed, 66 insertions(+), 55 deletions(-) diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 9c9b5d3486..80d34e8216 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -1441,6 +1441,24 @@ private: class ConfigOptionDef { public: + enum class GUIType { + undefined, + // Open enums, integer value could be one of the enumerated values or something else. + i_enum_open, + // Open enums, float value could be one of the enumerated values or something else. + f_enum_open, + // Color picker, string value. + color, + // ??? + select_open, + // Currently unused. + slider, + // Static text + legend, + // Vector value, but edited as a single string. + one_string, + }; + // Identifier of this option. It is stored here so that it is accessible through the by_serialization_key_ordinal map. t_config_option_key opt_key; // What type? bool, int, string etc. @@ -1524,7 +1542,7 @@ public: // Usually empty. // Special values - "i_enum_open", "f_enum_open" to provide combo box for int or float selection, // "select_open" - to open a selection dialog (currently only a serial port selection). - std::string gui_type; + GUIType gui_type { GUIType::undefined }; // Usually empty. Otherwise "serialized" or "show_value" // The flags may be combined. // "serialized" - vector valued option is entered in a single edit field. Values are separated by a semicolon. diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 3aad02f91a..f772f354f7 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -66,7 +66,7 @@ void PrintConfigDef::init_common_params() def->label = L("G-code thumbnails"); def->tooltip = L("Picture sizes to be stored into a .gcode and .sl1 files, in the following format: \"XxY, XxY, ...\""); def->mode = comExpert; - def->gui_type = "one_string"; + def->gui_type = ConfigOptionDef::GUIType::one_string; def->set_default_value(new ConfigOptionPoints()); def = this->add("layer_height", coFloat); @@ -116,7 +116,7 @@ void PrintConfigDef::init_common_params() def = this->add("printhost_port", coString); def->label = L("Printer"); def->tooltip = L("Name of the printer"); - def->gui_type = "select_open"; + def->gui_type = ConfigOptionDef::GUIType::select_open; def->mode = comAdvanced; def->set_default_value(new ConfigOptionString("")); @@ -568,7 +568,7 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionBool(true)); def = this->add("extruder", coInt); - def->gui_type = "i_enum_open"; + def->gui_type = ConfigOptionDef::GUIType::i_enum_open; def->label = L("Extruder"); def->category = L("Extruders"); def->tooltip = L("The extruder to use (unless more specific extruder settings are specified). " @@ -606,7 +606,7 @@ void PrintConfigDef::init_fff_params() def = this->add("extruder_colour", coStrings); def->label = L("Extruder Color"); def->tooltip = L("This is only used in the Slic3r interface as a visual help."); - def->gui_type = "color"; + def->gui_type = ConfigOptionDef::GUIType::color; // Empty string means no color assigned yet. def->set_default_value(new ConfigOptionStrings { "" }); @@ -668,7 +668,7 @@ void PrintConfigDef::init_fff_params() def = this->add("filament_colour", coStrings); def->label = L("Color"); def->tooltip = L("This is only used in the Slic3r interface as a visual help."); - def->gui_type = "color"; + def->gui_type = ConfigOptionDef::GUIType::color; def->set_default_value(new ConfigOptionStrings { "#29B2B2" }); def = this->add("filament_notes", coStrings); @@ -812,7 +812,7 @@ void PrintConfigDef::init_fff_params() def = this->add("filament_type", coStrings); def->label = L("Filament type"); def->tooltip = L("The filament material type for use in custom G-codes."); - def->gui_type = "f_enum_open"; + def->gui_type = ConfigOptionDef::GUIType::f_enum_open; def->gui_flags = "show_value"; def->enum_values.push_back("PLA"); def->enum_values.push_back("PET"); @@ -881,7 +881,7 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionFloat(45)); def = this->add("fill_density", coPercent); - def->gui_type = "f_enum_open"; + def->gui_type = ConfigOptionDef::GUIType::f_enum_open; def->gui_flags = "show_value"; def->label = L("Fill density"); def->category = L("Infill"); @@ -1168,7 +1168,7 @@ void PrintConfigDef::init_fff_params() "Set this parameter to zero to disable anchoring perimeters connected to a single infill line."); def->sidetext = L("mm or %"); def->ratio_over = "infill_extrusion_width"; - def->gui_type = "f_enum_open"; + def->gui_type = ConfigOptionDef::GUIType::f_enum_open; def->enum_values.push_back("0"); def->enum_values.push_back("1"); def->enum_values.push_back("2"); @@ -1950,7 +1950,7 @@ void PrintConfigDef::init_fff_params() #if 0 def = this->add("seam_preferred_direction", coFloat); -// def->gui_type = "slider"; +// def->gui_type = ConfigOptionDef::GUIType::slider; def->label = L("Direction"); def->sidetext = L("°"); def->full_label = L("Preferred direction of the seam"); @@ -1960,7 +1960,7 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionFloat(0)); def = this->add("seam_preferred_direction_jitter", coFloat); -// def->gui_type = "slider"; +// def->gui_type = ConfigOptionDef::GUIType::slider; def->label = L("Jitter"); def->sidetext = L("°"); def->full_label = L("Seam preferred direction jitter"); @@ -2234,7 +2234,7 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionBool(false)); def = this->add("support_material_contact_distance", coFloat); - def->gui_type = "f_enum_open"; + def->gui_type = ConfigOptionDef::GUIType::f_enum_open; def->label = L("Top contact Z distance"); def->category = L("Support material"); def->tooltip = L("The vertical distance between object and support material interface. " @@ -2252,7 +2252,7 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionFloat(0.2)); def = this->add("support_material_bottom_contact_distance", coFloat); - def->gui_type = "f_enum_open"; + def->gui_type = ConfigOptionDef::GUIType::f_enum_open; def->label = L("Bottom contact Z distance"); def->category = L("Support material"); def->tooltip = L("The vertical distance between the object top surface and the support material interface. " @@ -2318,7 +2318,7 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionInt(1)); auto support_material_interface_layers = def = this->add("support_material_interface_layers", coInt); - def->gui_type = "i_enum_open"; + def->gui_type = ConfigOptionDef::GUIType::i_enum_open; def->label = L("Top interface layers"); def->category = L("Support material"); def->tooltip = L("Number of interface layers to insert between the object(s) and support material."); @@ -2336,7 +2336,7 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionInt(3)); def = this->add("support_material_bottom_interface_layers", coInt); - def->gui_type = "i_enum_open"; + def->gui_type = ConfigOptionDef::GUIType::i_enum_open; def->label = L("Bottom interface layers"); def->category = L("Support material"); def->tooltip = L("Number of interface layers to insert between the object(s) and support material. " @@ -2873,7 +2873,7 @@ void PrintConfigDef::init_sla_params() def = this->add("material_type", coString); def->label = L("SLA material type"); def->tooltip = L("SLA material type"); - def->gui_type = "f_enum_open"; // TODO: ??? + def->gui_type = ConfigOptionDef::GUIType::f_enum_open; // TODO: ??? def->gui_flags = "show_value"; def->enum_values.push_back("Tough"); def->enum_values.push_back("Flexible"); diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 8dc939aed1..9caac57697 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -2760,7 +2760,7 @@ std::pair -1) { + if (num_interface_layers_top > 0) { // Top Z coordinate of a slab, over which we are collecting the top / bottom contact surfaces coordf_t top_z = intermediate_layers[std::min(num_intermediate - 1, idx_intermediate_layer + num_interface_layers_top - 1)]->print_z; coordf_t top_inteface_z = std::numeric_limits::max(); @@ -2781,7 +2781,7 @@ std::pair top_inteface_z ? polygons_top_contact_projected_base : polygons_top_contact_projected_interface, top_contact_layer.polygons); } } - if (num_interface_layers_bottom > -1) { + if (num_interface_layers_bottom > 0) { // Bottom Z coordinate of a slab, over which we are collecting the top / bottom contact surfaces coordf_t bottom_z = intermediate_layers[std::max(0, idx_intermediate_layer - num_interface_layers_bottom + 1)]->bottom_z; coordf_t bottom_interface_z = - std::numeric_limits::max(); diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 02ef877759..5af658a213 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -902,7 +902,7 @@ void Choice::BUILD() { if (m_opt.width >= 0) size.SetWidth(m_opt.width*m_em_unit); choice_ctrl* temp; - if (!m_opt.gui_type.empty() && m_opt.gui_type.compare("select_open") != 0) { + if (m_opt.gui_type != undefined && m_opt.gui_type != ConfigOptionDef::GUIType::select_open) { m_is_editable = true; temp = new choice_ctrl(m_parent, wxID_ANY, wxString(""), wxDefaultPosition, size); } @@ -1237,7 +1237,7 @@ boost::any& Choice::get_value() else if (m_opt_id == "brim_type") m_value = static_cast(ret_enum); } - else if (m_opt.gui_type == "f_enum_open") { + else if (m_opt.gui_type == ConfigOptionDef::GUIType::f_enum_open || m_opt.gui_type == ConfigOptionDef::GUIType::i_enum_open) { const int ret_enum = field->GetSelection(); if (ret_enum < 0 || m_opt.enum_values.empty() || m_opt.type == coStrings || (ret_str != m_opt.enum_values[ret_enum] && ret_str != _(m_opt.enum_labels[ret_enum]))) @@ -1245,6 +1245,8 @@ boost::any& Choice::get_value() get_value_by_opt_type(ret_str); else if (m_opt.type == coFloatOrPercent) m_value = m_opt.enum_values[ret_enum]; + else if (m_opt.type == coInt) + m_value = atoi(m_opt.enum_values[ret_enum].c_str()); else m_value = atof(m_opt.enum_values[ret_enum].c_str()); } diff --git a/src/slic3r/GUI/OG_CustomCtrl.cpp b/src/slic3r/GUI/OG_CustomCtrl.cpp index 6433bf2d13..aba3404e73 100644 --- a/src/slic3r/GUI/OG_CustomCtrl.cpp +++ b/src/slic3r/GUI/OG_CustomCtrl.cpp @@ -91,7 +91,7 @@ void OG_CustomCtrl::init_ctrl_lines() height = m_bmp_blinking_sz.GetHeight() + m_v_gap; ctrl_lines.emplace_back(CtrlLine(height, this, line, true)); } - else if (opt_group->label_width != 0 && (!line.label.IsEmpty() || option_set.front().opt.gui_type == "legend") ) + else if (opt_group->label_width != 0 && (!line.label.IsEmpty() || option_set.front().opt.gui_type == ConfigOptionDef::GUIType::legend) ) { wxSize label_sz = GetTextExtent(line.label); height = label_sz.y * (label_sz.GetWidth() > int(opt_group->label_width * m_em_unit) ? 2 : 1) + m_v_gap; @@ -186,11 +186,11 @@ wxPoint OG_CustomCtrl::get_pos(const Line& line, Field* field_in/* = nullptr*/) #endif //__WXMSW__ h_pos += label_w + 1 + m_h_gap; } - h_pos += (opt.opt.gui_type == "legend" ? 1 : 3) * blinking_button_width; + h_pos += (opt.opt.gui_type == ConfigOptionDef::GUIType::legend ? 1 : 3) * blinking_button_width; if (field == field_in) break; - if (opt.opt.gui_type == "legend") + if (opt.opt.gui_type == ConfigOptionDef::GUIType::legend) h_pos += 2 * blinking_button_width; h_pos += field->getWindow()->GetSize().x; @@ -580,7 +580,7 @@ wxCoord OG_CustomCtrl::CtrlLine::draw_mode_bmp(wxDC& dc, wxCoord v_pos) wxBitmap bmp = create_scaled_bitmap(bmp_name, ctrl, wxOSX ? 10 : 12); wxCoord y_draw = v_pos + lround((height - get_bitmap_size(bmp).GetHeight()) / 2); - if (og_line.get_options().front().opt.gui_type != "legend") + if (og_line.get_options().front().opt.gui_type != ConfigOptionDef::GUIType::legend) dc.DrawBitmap(bmp, 0, y_draw); return get_bitmap_size(bmp).GetWidth() + ctrl->m_h_gap; diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index b87e2047d4..c81f4c6448 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -25,23 +25,27 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id) { const t_field& OptionsGroup::build_field(const t_config_option_key& id, const ConfigOptionDef& opt) { // Check the gui_type field first, fall through // is the normal type. - if (opt.gui_type == "select") { - } else if (opt.gui_type == "select_open") { + switch (opt.gui_type) { + case ConfigOptionDef::GUIType::select_open: m_fields.emplace(id, Choice::Create(this->ctrl_parent(), opt, id)); - } else if (opt.gui_type == "color") { + break; + case ConfigOptionDef::GUIType::color: m_fields.emplace(id, ColourPicker::Create(this->ctrl_parent(), opt, id)); - } else if (opt.gui_type == "f_enum_open" || - opt.gui_type == "i_enum_open" || - opt.gui_type == "i_enum_closed") { + break; + case ConfigOptionDef::GUIType::f_enum_open: + case ConfigOptionDef::GUIType::i_enum_open: m_fields.emplace(id, Choice::Create(this->ctrl_parent(), opt, id)); - } else if (opt.gui_type == "slider") { + break; + case ConfigOptionDef::GUIType::slider: m_fields.emplace(id, SliderCtrl::Create(this->ctrl_parent(), opt, id)); - } else if (opt.gui_type == "i_spin") { // Spinctrl - } else if (opt.gui_type == "legend") { // StaticText + break; + case ConfigOptionDef::GUIType::legend: // StaticText m_fields.emplace(id, StaticText::Create(this->ctrl_parent(), opt, id)); - } else if (opt.gui_type == "one_string") { + break; + case ConfigOptionDef::GUIType::one_string: m_fields.emplace(id, TextCtrl::Create(this->ctrl_parent(), opt, id)); - } else { + break; + default: switch (opt.type) { case coFloatOrPercent: case coFloat: @@ -122,7 +126,7 @@ bool OptionsGroup::is_legend_line() { if (m_lines.size() == 1) { const std::vector

_Box

::infinite(const P& center) { // It is important for Mx and My to be strictly less than half of the // range of type C. width(), height() and area() will not overflow this way. - C Mx = C((std::numeric_limits::lowest() + 2 * getX(center)) / 2.01); - C My = C((std::numeric_limits::lowest() + 2 * getY(center)) / 2.01); + C Mx = C((std::numeric_limits::lowest() + 2 * getX(center)) / 4.01); + C My = C((std::numeric_limits::lowest() + 2 * getY(center)) / 4.01); ret.maxCorner() = center - P{Mx, My}; ret.minCorner() = center + P{Mx, My}; From 41c56f2eb8a48ef5530938da08909365108f686d Mon Sep 17 00:00:00 2001 From: Pascal de Bruijn Date: Tue, 13 Apr 2021 19:09:54 +0200 Subject: [PATCH 243/285] creality.ini: Extrudr NX2 slightly lower temps After more practical testing, a slightly lower temp is beneficial on small pointy areas, preventing them from being slightly deformed. --- resources/profiles/Creality.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index 0b4e4cf140..ead535aed4 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -652,9 +652,9 @@ filament_colour = #FF0000 [filament:Extrudr PLA NX2 @CREALITY] inherits = *PLA* filament_vendor = Extrudr -temperature = 200 +temperature = 195 bed_temperature = 60 -first_layer_temperature = 205 +first_layer_temperature = 200 first_layer_bed_temperature = 60 filament_cost = 23.63 filament_density = 1.3 From 8845b0245abf6da3f9881b65673fd947eb079763 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 14 Apr 2021 07:04:25 +0200 Subject: [PATCH 244/285] Fixed build against wxWidgets 3.0 --- src/slic3r/GUI/MainFrame.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 5c390b66f6..9c426dcd96 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -522,8 +522,10 @@ void MainFrame::init_tabpanel() #ifndef __WXOSX__ // Don't call SetFont under OSX to avoid name cutting in ObjectList m_tabpanel->SetFont(Slic3r::GUI::wxGetApp().normal_font()); #endif +#if wxCHECK_VERSION(3,1,3) if (wxSystemSettings::GetAppearance().IsDark()) m_tabpanel->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); +#endif m_tabpanel->Hide(); m_settings_dialog.set_tabpanel(m_tabpanel); From 41a0e270ac3683cb47590d53943c21a36d684051 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 14 Apr 2021 08:51:54 +0200 Subject: [PATCH 245/285] Fix integer overflows in libnest2d tests --- tests/libnest2d/libnest2d_tests_main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/libnest2d/libnest2d_tests_main.cpp b/tests/libnest2d/libnest2d_tests_main.cpp index a0f1924603..e3ffe9c6e3 100644 --- a/tests/libnest2d/libnest2d_tests_main.cpp +++ b/tests/libnest2d/libnest2d_tests_main.cpp @@ -1171,7 +1171,7 @@ TEST_CASE("Test for biggest bounding box area", "[Nesting], [NestKernels]") pconfig.object_function = [&pile_box](const Item &item) -> double { Box b = sl::boundingBox(item.boundingBox(), pile_box); - double area = b.area() / (W * W); + double area = b.area() / (double(W) * W); return -area; }; @@ -1187,5 +1187,5 @@ TEST_CASE("Test for biggest bounding box area", "[Nesting], [NestKernels]") // Here the result shall be a stairway of boxes REQUIRE(pile.size() == N); - REQUIRE(bb.area() == N * N * W * W); + REQUIRE(bb.area() == double(N) * N * W * W); } From 0625788583bcd98c10913e484fc2f8e5e59b4584 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 14 Apr 2021 14:25:25 +0200 Subject: [PATCH 246/285] Fixed obvious bug in move operator, discovered by clang lint ran by Tamas. --- src/libslic3r/SupportMaterial.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 1242df1ea5..08cd04b909 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -3186,7 +3186,7 @@ struct MyLayerExtruded MyLayerExtruded& operator=(MyLayerExtruded &&rhs) { this->layer = rhs.layer; this->extrusions = std::move(rhs.extrusions); - this->m_polygons_to_extrude = std::move(m_polygons_to_extrude); + this->m_polygons_to_extrude = std::move(rhs.m_polygons_to_extrude); rhs.layer = nullptr; return *this; } From 526233ca472da33070a0d576fe8144100e02cb43 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 14 Apr 2021 14:46:49 +0200 Subject: [PATCH 247/285] Modified version of automatic downscale on load of objects too big --- src/libslic3r/Technologies.hpp | 2 ++ src/slic3r/GUI/Plater.cpp | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index d6b2cff8ee..303ffe9274 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -57,6 +57,8 @@ #define ENABLE_GCODE_WINDOW (1 && ENABLE_2_4_0_ALPHA0) // Enable exporting lines M73 for remaining time to next printer stop to gcode #define ENABLE_EXTENDED_M73_LINES (1 && ENABLE_VALIDATE_CUSTOM_GCODE) +// Enable a modified version of automatic downscale on load of objects too big +#define ENABLE_MODIFIED_DOWNSCALE_ON_LOAD_OBJECTS_TOO_BIG (1 && ENABLE_2_4_0_ALPHA0) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index e0a5031c23..a3d30d5f8d 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2407,6 +2407,29 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &mode #endif /* AUTOPLACEMENT_ON_LOAD */ } +#if ENABLE_MODIFIED_DOWNSCALE_ON_LOAD_OBJECTS_TOO_BIG + for (size_t i = 0; i < object->instances.size(); ++i) { + ModelInstance* instance = object->instances[i]; + const Vec3d size = object->instance_bounding_box(i).size(); + const Vec3d ratio = size.cwiseQuotient(bed_size); + const double max_ratio = std::max(ratio(0), ratio(1)); + if (max_ratio > 10000) { + // the size of the object is too big -> this could lead to overflow when moving to clipper coordinates, + // so scale down the mesh + double inv = 1. / max_ratio; + object->scale_mesh_after_creation(inv * Vec3d::Ones()); + object->origin_translation = Vec3d::Zero(); + object->center_around_origin(); + scaled_down = true; + break; + } + else if (max_ratio > 5) { + const Vec3d inverse = 1.0 / max_ratio * Vec3d::Ones(); + instance->set_scaling_factor(inverse); + scaled_down = true; + } + } +#else const Vec3d size = object->bounding_box().size(); const Vec3d ratio = size.cwiseQuotient(bed_size); const double max_ratio = std::max(ratio(0), ratio(1)); @@ -2425,6 +2448,7 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &mode } scaled_down = true; } +#endif // ENABLE_MODIFIED_DOWNSCALE_ON_LOAD_OBJECTS_TOO_BIG object->ensure_on_bed(); } From d8f56ef5fd49963f88ed72a2baca0c11799ed1ca Mon Sep 17 00:00:00 2001 From: Pascal de Bruijn Date: Tue, 13 Apr 2021 19:23:01 +0200 Subject: [PATCH 248/285] creality.ini: add Devil Design approx spool weights by manufacturer specifications: https://devildesign.com/download/PLA_-_product_card.pdf --- resources/profiles/Creality.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index ead535aed4..c737915830 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -637,6 +637,7 @@ first_layer_bed_temperature = 60 filament_cost = 19.00 filament_density = 1.24 filament_colour = #FF0000 +filament_spool_weight = 250 [filament:Devil Design PLA (Galaxy) @CREALITY] inherits = *PLA* @@ -648,6 +649,7 @@ first_layer_bed_temperature = 65 filament_cost = 19.00 filament_density = 1.24 filament_colour = #FF0000 +filament_spool_weight = 250 [filament:Extrudr PLA NX2 @CREALITY] inherits = *PLA* From 10e914d9a935b44ab1b8f0c41fe012863db7fad6 Mon Sep 17 00:00:00 2001 From: Pascal de Bruijn Date: Wed, 14 Apr 2021 17:56:25 +0200 Subject: [PATCH 249/285] creality.ini: add 3DJAKE ecoPLA Matt the filament is very flowy, which means it prints well at lower temperatures. and given the limited part cooling fan on most creality printers, it prints less well at higher temperatures. --- resources/profiles/Creality.ini | 70 +++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index c737915830..80c76932d0 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -21,7 +21,7 @@ technology = FFF family = ENDER bed_model = ender3_bed.stl bed_texture = ender3.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:ENDER3BLTOUCH] name = Creality Ender-3 BLTouch @@ -30,7 +30,7 @@ technology = FFF family = ENDER bed_model = ender3_bed.stl bed_texture = ender3.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:ENDER3V2] name = Creality Ender-3 V2 @@ -39,7 +39,7 @@ technology = FFF family = ENDER bed_model = ender3v2_bed.stl bed_texture = ender3v2.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:ENDER3MAX] name = Creality Ender-3 Max @@ -48,7 +48,7 @@ technology = FFF family = ENDER bed_model = cr10v2_bed.stl bed_texture = cr10spro.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:ENDER4] name = Creality Ender-4 @@ -57,7 +57,7 @@ technology = FFF family = ENDER bed_model = ender3v2_bed.stl bed_texture = ender3v2.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:ENDER5] name = Creality Ender-5 @@ -66,7 +66,7 @@ technology = FFF family = ENDER bed_model = ender3_bed.stl bed_texture = ender3.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:ENDER5PLUS] name = Creality Ender-5 Plus @@ -75,7 +75,7 @@ technology = FFF family = ENDER bed_model = ender5plus_bed.stl bed_texture = ender5plus.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:ENDER6] name = Creality Ender-6 @@ -84,7 +84,7 @@ technology = FFF family = ENDER bed_model = ender6_bed.stl bed_texture = ender6.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:ENDER2] name = Creality Ender-2 @@ -93,7 +93,7 @@ technology = FFF family = ENDER bed_model = ender2_bed.stl bed_texture = ender2.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR5PRO] name = Creality CR-5 Pro @@ -102,7 +102,7 @@ technology = FFF family = CR bed_model = cr5pro_bed.stl bed_texture = cr5pro.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR5PROH] name = Creality CR-5 Pro H @@ -111,7 +111,7 @@ technology = FFF family = CR bed_model = cr5pro_bed.stl bed_texture = cr5pro.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR6SE] name = Creality CR-6 SE @@ -120,7 +120,7 @@ technology = FFF family = CR bed_model = cr6se_bed.stl bed_texture = cr6se.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR6MAX] name = Creality CR-6 Max @@ -129,7 +129,7 @@ technology = FFF family = CR bed_model = cr10s4_bed.stl bed_texture = cr10s4.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR10MINI] name = Creality CR-10 Mini @@ -138,7 +138,7 @@ technology = FFF family = CR bed_model = cr10mini_bed.stl bed_texture = cr10mini.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR10MAX] name = Creality CR-10 Max @@ -147,7 +147,7 @@ technology = FFF family = CR bed_model = cr10max_bed.stl bed_texture = cr10max.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR10] name = Creality CR-10 @@ -156,7 +156,7 @@ technology = FFF family = CR bed_model = cr10_bed.stl bed_texture = cr10.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR10V2] name = Creality CR-10 V2 @@ -165,7 +165,7 @@ technology = FFF family = CR bed_model = cr10v2_bed.stl bed_texture = cr10.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR10V3] name = Creality CR-10 V3 @@ -174,7 +174,7 @@ technology = FFF family = CR bed_model = cr10v2_bed.stl bed_texture = cr10.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR10S] name = Creality CR-10 S @@ -183,7 +183,7 @@ technology = FFF family = CR bed_model = cr10_bed.stl bed_texture = cr10.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR10SPRO] name = Creality CR-10 S Pro @@ -192,7 +192,7 @@ technology = FFF family = CR bed_model = cr10v2_bed.stl bed_texture = cr10spro.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR10SPROV2] name = Creality CR-10 S Pro V2 @@ -201,7 +201,7 @@ technology = FFF family = CR bed_model = cr10v2_bed.stl bed_texture = cr10.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR10S4] name = Creality CR-10 S4 @@ -210,7 +210,7 @@ technology = FFF family = CR bed_model = cr10s4_bed.stl bed_texture = cr10s4.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR10S5] name = Creality CR-10 S5 @@ -219,7 +219,7 @@ technology = FFF family = CR bed_model = cr10s5_bed.stl bed_texture = cr10s5.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR20] name = Creality CR-20 @@ -228,7 +228,7 @@ technology = FFF family = CR bed_model = ender3_bed.stl bed_texture = cr20.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR20PRO] name = Creality CR-20 Pro @@ -237,7 +237,7 @@ technology = FFF family = CR bed_model = ender3_bed.stl bed_texture = cr20.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR200B] name = Creality CR-200B @@ -246,7 +246,7 @@ technology = FFF family = CR bed_model = cr200b_bed.stl bed_texture = cr200b.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR8] name = Creality CR-8 @@ -255,7 +255,7 @@ technology = FFF family = CR bed_model = cr8_bed.stl bed_texture = cr8.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY #[printer_model:CRX] #name = Creality CR-X @@ -264,7 +264,7 @@ default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @ #family = CR-X #bed_model = cr10v2_bed.stl #bed_texture = cr10spro.svg -#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY #[printer_model:CRXPRO] #name = Creality CR-X Pro @@ -273,7 +273,7 @@ default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @ #family = CR-X #bed_model = cr10v2_bed.stl #bed_texture = cr10spro.svg -#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY # All presets starting with asterisk, for example *common*, are intermediate and they will # not make it into the user interface. @@ -708,6 +708,18 @@ filament_density = 1.24 filament_colour = #125467 filament_spool_weight = 238 +[filament:3DJAKE ecoPLA Matt @CREALITY] +inherits = *PLA* +filament_vendor = 3DJAKE +temperature = 195 +bed_temperature = 60 +first_layer_temperature = 195 +first_layer_bed_temperature = 60 +filament_cost = 24.99 +filament_density = 1.38 +filament_colour = #125467 +filament_spool_weight = 238 + [filament:3DJAKE ecoPLA Tough @CREALITY] inherits = *PLA* filament_vendor = 3DJAKE From c492d0d109a2150743eebf46d1b4916663fc64c6 Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Wed, 14 Apr 2021 18:48:06 +0200 Subject: [PATCH 250/285] Add files via upload --- .../profiles/Artillery/Genius_thumbnail.png | Bin 0 -> 42135 bytes resources/profiles/Artillery/X1_thumbnail.png | Bin 0 -> 36381 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 resources/profiles/Artillery/Genius_thumbnail.png create mode 100644 resources/profiles/Artillery/X1_thumbnail.png diff --git a/resources/profiles/Artillery/Genius_thumbnail.png b/resources/profiles/Artillery/Genius_thumbnail.png new file mode 100644 index 0000000000000000000000000000000000000000..227f7ca8a57c11da579439eebd07f9adf50079c3 GIT binary patch literal 42135 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 T#_|TS00000NkvXXu0mjfE)IFE literal 0 HcmV?d00001 diff --git a/resources/profiles/Artillery/X1_thumbnail.png b/resources/profiles/Artillery/X1_thumbnail.png new file mode 100644 index 0000000000000000000000000000000000000000..4aa3a0dcccebc7aaa852a8a82eabd9940a9915e1 GIT binary patch literal 36381 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%0D`01xkydjCR>yIBl{-U7p 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 BwlDwy literal 0 HcmV?d00001 From d2969e155837cc50081d68c4f64a910f1a7291d5 Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Wed, 14 Apr 2021 18:48:38 +0200 Subject: [PATCH 251/285] Delete genius_thumbnail.png --- .../profiles/Artillery/genius_thumbnail.png | Bin 42135 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 resources/profiles/Artillery/genius_thumbnail.png diff --git a/resources/profiles/Artillery/genius_thumbnail.png b/resources/profiles/Artillery/genius_thumbnail.png deleted file mode 100644 index 227f7ca8a57c11da579439eebd07f9adf50079c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 42135 zcmXt9Wl&pPx5kUR1lQuliUfCSu@r*4JH@ro;!g46RxG%?ySqCScX#;m-kF<8<|H{k zl9RRevmaRrQ&Ez|KqWzifq}u0my`MiJ=Vg&z``ISL7$Oq>Fz>b1X@W-s>n-9Qhj%F zFt@TbgMnd;{ShlH*CR&OXP}y|&5Y@W?TD%@f0>0IeaFu);9fa0saVx2x7x zYoAuxtmbj%OHfX0ecYbFm{D(4AVM5AbzU$Rb0u~@n6^(U{LX~pjd~!9E?J?R3&2mP z5`;Q*8Svh9Za>yN`!@|g9CnSp{*@CI!R?p%T75^1U5h8&L)I^rveOFQpG(Tq$9A8b z5N;gci5Bb>*WuaRe|9nGanZ?M&ETUVZoWpgGkri`h3n#JXqXpKqWp>OTIT~#Mh0gh zJ*BsO%q7*sO4R8|04I&Hemr&HMCL_`D|bmi12` zt2t+c*30{6(>s<21JZ#QN}|ppB$M8w_E#2PAvOSdFi!T*j{9}*+m3s{{i<_}$NA_~ zo<=|Eu?~wP?hiA)Cc!L^?V)3act>7Q(R(IJNDOeFqT>jnVJEoX({{g3YK|ftp}~6A z{%qfL-u`@pm*pcS@;D^A@tHy?y0k3g(9YrB8=MhhU(x3J?_w+7!S7jc-o9}w`1^$L zeQ2nH0Sy6I8tlrj9b|0Eb{n|CoN59$&@Qr$Dt)?>O1~;rrwIa zme!+7;mqq;nt}7o`Pz?ry|i<$1-+}x`&J!`|Go*``ty3$iSXN^>N(K$Xm$gP@j-US zv9SC|NrfmfRvdq6CPQgoRYT8p*H2UTNcd_!>mlX60OGqL*dh4iTBG%}h4RNuj=RX! zU{8hbb{K<#-a;-!dx2-;>b?UJm%jYLk5AuiBe)4n)DJ77zd}C zwGSW(N+6NQW<*u2Q6M5Bq7ZgFIk-RH=y(ddZ@+IdN}8;B$cpwpZ_{AYU6iZmecotx z^EwsA#b1tN7?;30LO1eltU94I5nC2T zS?Dry)$g71O4jej;3l=Cf@ebHx;1G|sz08y$akuwm>Qb_1txw7`14@>hc_j^)6IdX zV5Q&tzV8cz@8|@s7vcNN<$cF4rQbxcYT>%-g8gO5+kD#Y4yB*ia#^On^O$Q~{-^en zD0iowsD;nO8m$l8TG153qutwZ)ClC5&n_)?goIw{`hper8}Be3w_OIwTk}b*?4LL( zVu~SToc<6R8jS5sUk95{^V(8@Q?mb`^0XzJOl+T6v3&(@2KFYy8VLsb9J8cZnq)t-3 z+$eSkI01EpCI$Cl*aMt(&H{kw7zDF(48GPU(GACe2>Ap5M)Bt@fw(yH5sP!Pj8S+ zo|*T?PTIRaE)IzF`MtKi_o^z3D5TOEQn5QNuVK=#6KLUd{Xw?L-P2>yR2--!y2V369%N<=LCK1NQn3VjC?=M+x@j#`< z>)QsSsNErktenI;O>KPyDuj;{U!~B;HCR8fUoGw^U#4}u=?Gmu1Z0Q9=)>#7m#HH` z0JMM^F*I6E$8@-AojPhNI1s*3RP3c8F16FedFyT2z26RByWR8K^Z7gJ^)p`$zR&LV z!{ts~Zj=Zx>X#1d~ley;rM!>!6AVx^e~y%Ha1p9SqiZ(XGJ@_|8PZM@JO$&uFmiD zk7`Xn_US1Ar$;;sB`Lpm9bpW*>u3gf?km9{TEUdo_2H@e<()~xd%j&>mML`Q^9 z+Sjdnot-@Lg+jqzykTXppVVRdVoQ>hjSWP|Q>c_lk3b!!`t(YjmbRj1w)wJzG5etv zEnUz%Q?&gZRwkahyj&&2*bH3C`Pb1uW!`kDctY@lB&D115gliD!E5XEMe$JuOyEcc|78mmPUTTdPoULZYF;i$ z^T%axF}q#<62^VozDV2i*;h)hQQRkjNLBXCrxvP1ItNTQ*%*qQWt$2MAlb}$%z?wb zmX4qzK7*|T)&W+qt-~$9-{a|_WNBsY?+?rl zoSgI#y!9+ZU2v67EdcH_mY{;ai+sNsBp?%V39K%f%8sKC!>oT^YO2}qs1yI3?3);E zZuM}r!{B$-=CmE8s^PnZhPD1}q0X^9L4JKd%E9Mzxw@Kg{7@jY4b>8>8L`7ARjA=S z!PtdNb|2kRTR_R^NfZo;+$ml4MeGz;CLg(fb$ROdd5AoS;PT9MJx zAOww-DTWXI9%OVq^*kl7V2Gw|ttdLeC4v)2;57Hzwh;&_5)Ux68~w}5s5ivJr#r#t zcP;vFenme*)y!%_zx|)90~KX5AWXs3ZgG8G`LDeHHD=`2Xv&NxEr3>&)Ny94 zp|Yw;{M0(q%2Gi3k_1_h1QT{2&hVnWYf8hwncUzNY#`gPlIMoW8QjX+bUVMIsjmeE zAhLmP>R<=E)6rw-0zy**XZk)J?q%p)IXXeXt&0Km})I>s?tPj1GVXjEF5rA&Pf#JN~kHbN5gS;g!S*CGtW{yvT) zu{q3AZszEiIArRtBp5EOHGS;bv=c2@ly+_hss7Ty%V%)CBl?Ed_BEp&x{yZDai(t3 zR=gq82BP`Bax;-i-qB9Fzx#2*3q@CHl#HuO=ji^5#Yrh#;b^hgxsXwBmiUi37s_w{ z(N5IRyJe1}Gv^|XB_ikwS?kg15hRsjH7wA8QwAqWA+eZy!;K=*K1c18c zD%xSA5O|Q#VYeomoJqZ#dI|p2NN$Zp3b7-^=1J>Qs9+S7vm_-aTbYsT>g_;CaExqh z;@iA6h@)g?lDHQKIVfT@H8tr1VQ6liR~jKwIymA+t{eKfgIoa2?{zkf=oq2MW?EBL zb(&gQIpx-BY4sif#ll#^55!kPp(_UWUU#J@Jx!_Hiu~@rY3naSAF#-`SC_7F2da{! zJvVQbm!XvCd?70Zd@#cOA5r~G5^YB&%ZG4FIN4loYULQq7>-P>lJwD4Z|2;g#{HZ* zpbN(4l#OL8oi^II^&Y^PF6f>_XV)ujm0I06iJUCcLp?8p0Jh$*oZk-E>3nMgYD`hu z!YYum6IKF9J&saj9|mPb2J>P>~B1qM=LBA>Crq8cG$Hz|Eyb-MW_6 zA)FtY+7Ws=Ya;9mQ8QxabSG%=-!DD9`Am^%}|fvmM;u|m62|QKp+Xp1&!tePfii}yStCM zMakSirV@WTtEt0UIaSHbWB{p38K_vk25?U9Z%q3YA4Qyeo$sZ}8Kc5Bk~&aE)XTf3IiM;mLG&AUkS#+fN1*7oz_ z(bnVOY1vfvWBU(~P|X69O{BlCs}g?H(b4%Qt(2M%FhQX{8An5^0Gerb3&`L30J458-3$kh>qR_a7eMDf1y;yxJ%rV=7`=>MU*g?&l{e9>lT@GkW@K zHO#JhCGMGY@}WBbS_#unj`-AKalM*2+FIe95~*~WHeq>xxT8bMe!A1iOPPN1jCI}{ zC7$P-AJVoH$QZ|WXE{}uA(KVemz!~M2h-i`7^HR$6&+tBe&V3 zv1B%nLb1;DsH5uh)+ZV%(CA<0ON3mx!DSSAT+U2|A_!UE^_1uv<1rC$ELCrRaB;Sk zpB8>Dq$aR^lo1m#G_P^azOHEaizKq56BG2hwmpXV`EXTi6leB$gSqA9;sUu9p;eRO z?GB}uSKS8Egqd9B{`>VoWZVtf0dwGV=j`A4OkLhb&D~92Pm69EFXwGp)l}fT=ReVP zi-XgrE{x&n%@9D$0J^)P<5nwNu6NOv>5W&RC9D0y!rY0{|asK{_9m0-QKF9 z1szE;n!F`0ca%6r7T+vyl@vWBQij&w1G0>x72sYz^}{mn@!)n3fE7#!YQEN;gbAgg zajy10&mVAuQY15(q`zyM1m|S935%L1YC^T~e+65wO}TQ`>{Y=87jZS9RQi^XKBYoQ zkPm>`JP=zJ14WuuE3*OxYzjZbEvPg?SyONie?*S@fhWETEE$KCU7^`Bj znfuTlusMEa_+~j~e&!S1K;Qgw?X29tGY5>;93?cbT{A<>Dbz5f^s$vH5Qx}&V+=2_ z=KnF&6&f67*{~8RlUJ}yKA;**O%rY`fowX1T8>)An)gSk+UG*DV@B-_V$79U*;+kX z*03(47v=f(xX8mDAK`^jD2wGC>pN#$`bluOD|%Nqou8|MJyDab`k!=5Nl{`%AlGLLL=qim8s)g8C@2lXOu#V5+OLtjvEwxb+Yy)#v&bZlYHQVnqHU{`! z+Z^Z-F2>{ON)HcsKVn6Cg%h+U$Ki?B@4iwLKNqyzokqY zTKMXkGTabc7l7tmL&f2^v`7pXdv@SFl3*%>nz&9 zP6MGAWF=;m2ss2znH~S^aPU`#4jIl|r>eF^ z)*SYl#l_Y1+G^f;ZJvG=@JCn$;a|JrsJn>p2)N zUH!0{m1)?Yb*u;y-dtY%4DWmSdd{FotCS_8n7NNJagv(kg)g05S=WZh4e6M%P^xKc z99-}{)d55YzZ`YEi>9PyF=R2rt;mN*=Bs{OL|Hy;o^!uNt~$ zP~sqq0YnDRP_HB-nMZNrd;SWQmfRd&ssWxCAEh?5?jASp)~~35r@237;s2!msuRyW zrp5*u<@|ck!4+!%<^*oajOnkP#-|OgW=v6Vl2940&Sm^^w0w$smEjC9Aw~~Y#rO~@ zV-iOvp4cxuTOh}AqNlaB7)vXsvbRUHc;bME&=gLFOj!m}(aB46Y!ou4erd0(o7+4%>{0S)?(*1VZR|LNM6GBX?J5+jq^;!RmhgRZH(3YzHZHKsq< z5`F?DKofqp!o?*SaMG@f9}p0R6IloK&0f2HE+a-N0+g@lKSy$mGXQTxIT72&QFxOEtO_o9d+nY%`nkF8Okc)uu-HDcTWS0!iw52%P&mxWhvShP#5oh{yEF_CI z-m9@@w@4Yh;boKDz|$>cJXu5Zyvsk!{70MeI>hYl4>V9-x3*ypB%Vn*-jewI4>kHW z>bh3YPXE4n0i^8snX#W6zFwL$XU4vUgD)gdEU7NcwtM)BqWKil6@e{d@o2zCu<0b)V z5UklJpO$^PJJYjT42N`wh!rt=Ww6BvU2#0$pIdNK)U>rJRxI>r3-+y!{jzsOZhtW)%||BRH8$H&CAMaszJMqK|*EeavFdc5?N?>X71E$ zXct+>DGl1vGjwz$-cQ`Jl!(oIztFULg$oq^;dOyphdq}?I1Gvgl&5 zw;{F54$4 z4+Y!K%E^^LBtB9R3Pt~|40}RXbh0OhZJo9e zH;Hg2-K87?PyfOE9pQ@3iWI1Kb(O4VtFnqtQ}-_;Ds(fOa4v7!OD!;pGmE;m1apdt z4y>*UjeAFOrVF{oLDjhw+=8yNu*3VE-*9H)54C*RfSfsdzS1GXK}#Wf?S+}FikU+g zMHXSpKfInoc~ie>*bnX-hOTpS4oI|)!h(W=j=s53XFz3L`LsmCitxf&Z6*Ou-8>gX z48QXkI@G${+!PPaD-djKZEaOuVj79t-Q6uV7ce$4akg$`Bop!|O2O2~860T6c=-$< z1gj+|#!GB+B$q_7TZbTGke7 zP4%#M^lf2IRUS7n+3DOnX_8&^U+1s6D7r9;&oYsmGGKTH zsA$lU;MFzml|29bbmnnA1v>p>oTs-$Hg)VZ@Y$+StLu?Ts%8Va+yD$N6a6CSQ@W62-1R=Y3LzpI8XA;(8*&(WdZtV@UalY9KaWmK z@ac81?sNx1PxV2|`86U!QIbB~<1WR*^KzDHxs2Ble@7R(@;;7yv)0%NC|o&>tJ7vD z*OdG$J|2zfBH*lxOoXmM-j_lsjlur+u>0 zkp;ngfJrQQys;380!XG8gm%+n2}>l^{rp)MZ10{aly!2ZUM$&(h@~zU7hG>sTaKrz zclkK+BbqT>Ma3kt*U`oJyj{3Z_ms_wrE+O|OoPh4(dY z7Wo}a{_?x-Ie*$3V7px~PMWMx`HyX2nRAduLQT!3j*vKn3a%7|%K6I5O0kwTqaQy4 zf`SmQge-(8;?1JkmjIGbdx&vZ55qOJU~#b8?FoP6D~qXC|MGH#(A`5`WH3Db$7mX8 z?DNzkA_Bz!swIfjg9Fx61!vM!G(!C(vB2uVut;jIu`8vZ;%LG3=C>z}_Y(%u+c0Nn zP=MI+5b;he=(*j?Cw$q9x8b@sx^yf>cO@c+{Ht~F&`t1WZ*P!+Uq0?+`9xr!G}W}Z zI+vf(9!EL}jcDBYlxIOD!KAuY8l;YqMMe?Cmw2#E1%KTRk)E6mKJqXK^(W@)OHedSkRqB)th_*eKG7ix~b3 za%E%XAXI!qRh}x7g|r?4^qP&%8n1b|xd|FN<_b%{gy${>u?lwQMVMvwxiGH+B((@i zOG~GZZ{2>JXIE_#Tu&2)iF5zNg*r?WLD_zbhvv8NSVA1mO3h>@Mn*=y-03$ei6$EQ zLnY;HsiY1~DHDEo*C$Ig-4k!C6PzALO!W=Vnl1C(>N2#^WFj)LM5AM4HPFsYTiMsr z)%=@C*LrK`7n0`CmZJQ=lyx=IaZ@tuI@b6n8xG1^fYI(5((W!q0(B$-d(7C9JCF>Mfu{a72g~>O2azWbRAe~l-bVO;wirKo z68AQ!scMRbOnxyL&va&GJMWW8YGp;vZT0Yqddr^3@_TaDv^jKZk#Zc(6fS9*QyPv?Co83mlrcc-c*+l)%v z$4AX?*T5={>-v_~ZD<~o#E|LX1su9&H+(oco%$X1R<92JC}_?l4aSuQ)$9%<9C>y3 z_j?lV?>UO7s1=6IMCMu&PRPzXy}fusKda7Wlza!6qcwNeHWt3eDE8{B!xIv=#nSI4v0d z%kbXNozoI{y%*q=EODfvzm%miFi=J;+G%X5MgSL6#zcV2j?K*(L(EQ3&J zNfZZsG^|)?qAPCE3i3tV=JVa*YamKaI@4Nfc`AW)Y1stSsYmeuY}|fiq>(d4JvRyr z%q^^nF$Sx}e~!GS4%i~}6zS3)fl>&qq$-CE5kf*j#_-n5+lQ~wGGp|gS_HAZ9eLUx zoY~E#*xKK>!-dILQb!8CPKp0DM(#*bKAm1>7&tw5EY~#O@W=kFtFM~*CLUTv|1nDY zFYnF~OZncI!5h6gk!rI!IZG1jm|VPVauwr9uzUOWEjZ||{m*k8nlF@Fs{|myTNj{M zyKN2j&3QDu>7#QNyssbf>ZdQN&iAH7e7}dfc`5yR?rR^{pSR{T3|w0`&i($DGZ1IH zuR6B$I*#`5(f7Q;xMspXV_l4&KNr@nFm+tFQ0{5?J}zLdj1m`EeIXm@{F+uaT%As_ zNAhKS*`*n*9%ft+HcaF=J=5WOB--|Q;Z>T_!E)f@o1id4Rczbuy6UfYu7AJ>8jS0& zXYMxm(*54=`yrU*PmG2F8N3^p-VZJn^-R}6-|%k`tQMf(D?yheW4%GV^N=! zgzn)S=cz}uO||Z%m!rvLa^*YCC8e}@hSo~;eXWv>t{t~>d*>1pccb7x++~#=PxHi- z%Iq?xkMpHdRQ*&E{(;ix_r_xe)^*B+ zPcT%AUJV8IgkU)zJ;wWNUB^^AY#0&l??a6w0|ZjHmt(g$uR=IJG@$#YKWyDtkoqSQ z^sW(1x=X%}y**{00m1gy4M>R+-!}a#4efPDV7#={-?edyW0z$`f2ezT^?$P%HLTb2 zaU!*~wI0po)~}r(M6yL+XUqld%`%^8`(VAO2rdQ>CxxE$8hi zNhX9*WY=H~eb4%hb5ZhvOA(w{(<3skgHBi)6ZedlvN|Z^gAU?&1@GWMli-!IbZBC` zu;#}wR7l`sA(^Xi$m_Nhc=m?d9=XW4U2Kt!( zo>ovMVrb~uQcd-eeceJe>*toQfojO*oL)sExrgHfBpi;sH#8 zppHErzAT`J<$1HU3P%wtH6vtN!6U?~4?H)Fc>cpKst&gm*MCD;L0>Hc3@Jt(nDEV28z z^%Ce~5j%BkC^OU2p4;>*j-x3>->UJRGQH21n3BApgG&d{4S%cgZu$7uyW{?Yk}OCK z0V4%qqff)!?E}B>cN_e=xb%nIA*w66{c^@dh-1{X;m70wGtQVRH+Ru^e)T}fCzGY37#Eed?U4C@X^m#zb()$ox6ZmF; z?Y`k$F_ryZVe^w+`Kc@7V z$Y@w~Id)bSv3p+m=$hC6hl?_LlUzc~acYPtv;7r{b{7QWA;J+OBOd2V4Hk3W=Oe%? zQNC4?+puP~lw)c3x9*C!ej<_AiYj)O=sxfh);CH7O5@@TxeIZpG27iTl&-~9o#WX* zo3UTYGFp87R-;sp$@^)iIv&-d8seMIbxSZ;Ti2Vf(L^89C|^(YZ%+z4_Q^N&Ug{*< zNBDEz8?xTv1tW<#+dM&tl%1YAH^I%QvpgO|55xb^t}8k&Hz!=CPnH=nJufNS&Tq!p zXX(30Myj*q2BPS0o3IV5SPJY=Z6u8PEky3?@z^up-A{uOXCwLS0O{tA6L8}wpakrd z&>_=Ejjh9drK^)6qQPneT9iCpE&X(cqRB(I1D9lOUbT@C?^c$Pe^dMOx#GJhu*0oO zhcw0)ak(H;tMi5R)ifYoFREVU-=6qUb>zqT6qd%je^9KKc+#Q3A!@4?;_gHr)YhxrG^Fg|;&<)==k zR!4)UoAbx}Rd@jr((vAEw#P4q1yQhRgDpknmgaKF_K;jh!RI<1c0R;wp-Q zjvk7-$y4fbnH2KcS|<6Izwt`MqmW7=oK__tjUzADiEXxL!FdEBsrpqPL;7R7{*gJh zVVY&B#PEAwY0-O1YbB*q%GOqRu}HU4`uO*9f)c3b4Q-=f%wkoEc&KL)ZslfKNhJ`% za9imXYh?xHIUP}rL#1%=O!EE=GH0l#aYxK8X4CxEmYmhR_(BH_9P3S)kdgMG5s*VV zlkK-WLJV4VBqs7vIRss9C|LW(`p%4rq8|0=g##cU(1Ky3{=&ljZSelbE#BwB zOVob7f%C+fGQP$5*YOrn%5op?6Ja(nGGZD|Kz>&?WYdP*LyQyZ!`d0?3nw#yPUUF3 zQp8LXxRJiXh~}mz00|TlCb=>fQC*S;bt+`n5XwF@8jEo*Qys6OE5D1ZyfW~+^@y6@ zHuL4u7qA5xI_ZAIC;4L|+uOhW!_e7T0TgNFj=Rm+UCfa_3*clOXZRb3Bl~+_@`#G{ zeh>Sy2q8|cXpQ*7-(8|iqG8RbDorIxzfJN4AxgqO46#h;VGm=cFsW5vJW}o=vl*!u zKnrV=2p16hvx3#0^#r9ZeU&2ZvuBH5?Q3Eu-oSa2##!5b<2ZDnCDeVxTlc+htz%w< zJi67&VFhu{JW4qbyI5Q*jUX@=q2@CV?r-eCO|zUt>o&*WAmOoA{R?b)D!B0GUd`B0 zxSd1@pt{-*ONP~TmpBrQPX2qD^z=OL&I2}@mb}Bn?M-;NzB^?eXxxX7I`ylg3&@~G zcwEbtf%Vg9{61n^Zzb5>vJRI}_3L4$rx}^CS4OCP07mG{$9kv1<3F=9ns95i1#{Ye z8C(CACvlnTG75XwP>2g3IdLNddn*28l|ipV17bv-)bTqTMp7eCDGV8abuW5B)3yMW z{O`X6s2&80)DxuIB7S$V<`{5Zt?iek|K&^8ysSgcH6|q?>dT%fE5}WZ0?~xWrzVc7 zWC+rJ{puY$9l5MwC|(MSakKpCNoU1_-SAV7qc}MNZM0!_&7_HqO%QTPpWW<^V4Lc`tkahKwH?Xj&Yku z;jCP>#*3rEu~y~K`LSfQV_IIm2CE6_q6!oe&Qam^8-zMQUTwVjL~a907a|;XEoTi1 zWUR|MwO6j@Vg2lCWlVgIgd;Z_lVGA~@`jeuw?)Y5sk5}|>WfS#Kpxk|gyol9DYp-Y zy2@w0Lu_%=ph1$j#mdDm-+Szm_FHNRy&keH|5$&L3d>EXVNk})gHBZ1+G=U*=R(Mj zFuGV0?;{6jFNF3x970&>lq00wFSPGqzlyz=@te+He0EsnAFjEzZ6w^ zu!0hBE$niE)ZysV;hgmJimJK5T=ZahJNK1;`ibIO#GzfIu3DOlJ?m{Ft3HM}LJ6*& z-hC~|EI4ISML#+}#9&eE3ym{@Pm5a%T{4FeS8vpn83s$JJQQk7bYap$fi}FOU3{-+ zVeYRz`$r4yyWW0#%GFSA9@LFE>)%fk`LT$gHx%22d2uO)83pz>XK6<^yZ+buFa?qs z3{N9Al}Duf;_|Aom1m~9d$ccqaM98>NhM%~HDiE9-XhbE5@sam^3PloX1GeoPsrBe z%>GhCtJG}9{=bNQ&83bn9N5_DsNyt2uvHQqNDc^f^-CNqsW}gd6fNa(@RkKHqBG_=iiSkDFKqlWxespn?>v zVw?CP`?Tjo)b$+3t%2A2{P6@G!lbMibtl7jwF@6=!5kFpr zuqN%yUMZCW)|LJi9ZS&D(AH*~an* z?Lz}EjS`7@Kk102TIHlXU~hBiyFfQm7vO>hNJ}QzN=as`)L>C(lJlM4Sw5IZS&Zt- z;7IBq03GQ(VG}W9T;^;!?vcI<@w<{^y(e2NDm`$PUy8aL>q*8n)H^PWPgJz>|j z@8`Em@FUqQsGQz}=ax`$UXiU|Vdtes&uXxj#NA`$_4oY2Yt0FlzL6(Z$7(~VC8&2$l# zorUSfk#SA7@e@N0X(~~yOJ3q0TpZob`yoOu z)e}87Y3Y=6|LDz}O{63Oqwr|*LaQRlBaeTE(fY@(DS*JE(s&1B8LH8#NY&DgvkD>7 zGtOZoYKlPIWCjO)&&v-R?zhLjOE*~%bHG!g*K~g%_k&@nVc|(^mm$0dK`WRBtguMT z%L7aex{~O#1QyI3V)t>)DI=F-y6Ak;J!NVeAR-79ODK?XRM7v2{}@b{tVeazn>qX|9B5; z`p_mEV!XIvZf5nXb}c871f6H6Rjgp5#EGY842?|tFwXpwEeTG9ih5=%gcDRcMl4a4 zlfxq}WhM~H!nKA|K6nGOXwMKxH4Cx5K|`V~q*}(PYj@l2#d=-F^6UHH(0V-ZZ(!rK z0yK9p29iB;=B@c^hjZ0z^qJ#}mUnu@LH-xxH2V~YF^VQgAR{`OftEb+qg9eI z84v|u|FXPSgTPhmSd(?l?Tfx(@HhxeFh@54K?Fr|KL~wMm2vHPZB{Xw2`z)zHhVKC zj9n>^KX-oKe=6#xL+}@WYBJH~kMsu>RjMV%hyOgStDGWJ3gnvohbLJ?o;ZY!CJ{aq z$Q@qP7xw|64x9ITSs}NvNrd0EH;h`5wctvQ;Jbbrd=>NJAy^Uq6rVXrHfw3`W{;34 znBjNs#8mx!gMQV^x{2Eb<%a8mqvwQyDwjc0*?G%ZyKHdtKsr;!3q#f|-|MkURT?xo zwoa2&2YE7E9Gd;&o@Y|Wd@u}+G@K3LvIKjVbQR&kjWZp=$EM(#q5=XCDnhpv{*s8b zLQskTR?%tVRsP|ef)Sjg$H|*vik1($cKGg5r^&p>bP_cu-~i)q6*BN1vn31&h5d~5 z06Ru$aqujH@^_`B;_@7I7T@I!OIEoI7L@Yt&@RLfffyv|NR6*z8OO8vX(e-^s(sc} ziF+s?z}A7)(N*z^(S!R;6QVNH@;z^D*UCs!4eM{qX_+1OnJupd$8-19$N1~+G402C z?v9mqqJQiiT6beR4t#g;0(3QYs6GfVUH7XzrGA)*(d7ER>lAV7I8>tp-?!jo>3JB| z#A5#vC1;%saO`iyo@D{X0TF0nP1LTZkmgKJBDW(qv8NWl<;RvuMbe0uO<{-K65@4Q z;x8G}V3&cVICHd!f06VG3=3FPwh5nF=JMjs$&({s5MHX&Sd?PTBRFs#F10pDH4V6Kk@o!NvPnYC){6t>6_=(Y88u9iII^c@_Ah-k7#G54yg-P z^6X~6d852r7gDOJWybRv_uOdl`D#my(_~RLi=?TBB261ncEeH6WfvpB8*TGmyMoiz zZ=aBJ2#FkJ{fi^shv;jk+z?$v8~JJZ4%ny^5jxEZRzXuU1DmVc#}X5J`;6$V-={zB zwB0|qEBwwcimbnicz*s!OpHUUw(foAIpI0(oXMbHcRJ&8=-_Jr{j(53quIu%&{FA+ zgPrb;7ka3UR9baiTiBjeE&P6&*OAwo4Sy*d)A}+)>G!-Mg4#j;UiN_y+SZ@&1-pl%|}`$ne`cqFY0U z4fDcUV*bNL{J{Ek1Ja7%`X#Ry)WXlL@IJLkre#WWCA7#2uXMlXc009(Mc^jE&~0u+ z+a@zFFvpQK(j<@(__f?jzRj~U6rzqXnZR7gE0I1*Wyfb253|r$51V!-F>M<_i-sp1 zn)V?KxpF8$ROFtd{rtkt>pl6qZkxl_0g<2oQ+~(AheBDAHrUPHixC4QAD?5<_q+-L z7Pl7uoVZbtF?Z@G8s5-cK!30qq7AK-Bf2rX4J}<5Ir#fGUUQv#^?t7GI`wuw{AgVo z4&)>Z=pP4Zw~<*9B9J*5DuX+N4bh9k->Xw&o@3Y!>YPSksFdRTrFd9$8-MguRdYrf z0q_vZu)hH$k;#rGUvTwuvMTW=Cwv%e7mIk*OQXV42$08!G{h(zFZwZ?9o@32?``LN zyk&w3gQskHwtnzo$I<8@Iw7A>os=h@NBGOZ#&UezKshMUaYB#h`Bl=QD8)gRAew>^ z3W$voBa_00XON7VFA&^uYB~Ce1j6TqWzG-Gkz%)0Bj9<-Cn-Uy3jSM#H>trOiN{3z ztztXl;S05vJa?yVr1s_{EUO_dfJT!8j+P4@xBpNI9>(MoC+8nnV=NWMHUO>DbYh$O zB(hPFSCPlJHHx;w%h}zc`eOLaf-bVMaAoq?bs8i!J*qnZJYMVDX!2UDUcj2{;wLO0 zOH^dTNw`Z~|EA&DQSt>$eTs!~$epV~ICKfx{v z=gF9+)GS+^MU3h0;=M8~k}4SY386^1aVucS7&|cyK269GG4tG(7LRWMSAW89b0qEW z`aUPE@HkO=Y9+&F!l9w)xE0P6+aXU>DwLK*InnF&`d+ry2T<#eLIl7wst{bpB@Kg~ ze$9x6Wr+#?MNQ$6sD_&TH&+G3qMupkziD8{^NHqyRwAZ=i!$y@9aC?G1kXihAB$Ro zY1-MsA_8~K+JZHY>QA{=1{}I|0>wS#Q*~15Sg3@-b4*7+h?NEz+Ss5;$6^6HM@x55 z*X9c2EUDfAi*6G7Nlmx|{Nx871V`yURm$qv`+6L8r% zl}0M@nmT$tmpcQFMu%`x2!-S6y*U9NCT;5`^^UjFg$+{PE;=);Z7XuLnY!0=eqxBX zVzq<{6e(=B8EP7_*b!A-r(zsDpS8rakiNFmWIivjB{x-CimPO&q9V5@c>Jr$>OWdO zqS8bmjEErm{g;WA)oYCnYQY-XA<%U4hfFtBr2;U`3JFHZ1X{nO^^l-x=;)|z9JYl} z!_Qe~ffC*5gADpyA1;jCQodgKK0f(2ypv(0g_x<6xEuz%nAs(=>9ztCymZU#DeK#w z+sFdpq$1~M@s06hTC~(5hNiEUtlC{hwlygJ$6S-i^c>Pu^lCI7*dPyBD)GjBu0Ycd z_0Qo`=mKshe~@p=^$YLG*S#;x|4tM9(+#U;Rl!HRqA9#4BJ7&)lpzG+Q-{-Nl6C>m zOlUoD64*2`HOMhT|G>!z%Ov(^SMd&y!lqS-Fz6#m#1d$5BToV;>nB*K?@o{S%bU>k zdaTyQ=kITyUy*_ozT7UTf&U=)?hX0QXDP>-HnWb5aM_>k?sJSvjS?WZHaX8~npx5m zd5KB0gxddUX>*5#pt(2Wx-eK0T&y6j=s6efEmoO$nQjd#Sk&B$GA2j|`dbcnGRkJ$ z`&wIHSuoHqXYux1k_`xqPP$c2jV8f3!T7rdI-7Y^Dwqs6lS(*4#GNHsUUw%69okw1KoF}FM?|6|ojXF~HY zUovlM(lO|?XxWc$oB68-d;zm%U3Wp9HF}RJ&)}s!QS#0*`horXV=iv|BtiQ;M4znB zaTPR|Z=TnbPLGl}Zly;QliDiF>mN#az#ah86KrkraEW$sksA=Ks1LklZXc23SHI@3 z{q|{XXwsl>w{-=J3L^Yq9bJwLZ!H&;Jo6XCJ0rsvWy}Fn2-Az{LAvhkLHTQl_CPZT zBg!!$l#)B>sIGLg2|Ei(-&Lh$UP~adLl_^kA3Kt>c?RE?2Uf3nF$BaC#T!9(kR>K% zmUP?3GBU;(?q8mPg+DbdsA*I3^KSEtB%7%R=J?O>DoBpizr61T5^*ZT=Ic0UlR5H5 z!<}YV zMR;{5P+#h=_LN;n5vY%{LP7XLR>c)BPN!+0mK(2nrkhxV%e4@ZOT7JM+=siK{-4rFTEUIJDwwF!T6eU+DZeI2g8u*k9_nXC7u(qB`@zVe_i($& zU+TSZY_Ub4TSlRKop#wr@a=fhpbfz!t@z?ZwO;>4poi!nv+j8h28kR+5~G@5bT*{^ z4x6mw=lpPP50;Yfoc5MguPB*hBioDcH`^JGqtxykEJ}SUn96xJ{(arU^dvK4`T);p z-wtK1B&))isKq~?-~@Q9MVzq3CyliwoBZ|8kV~V6+(2Go?8{InIB2%N5GyUy&E!jB2Am(5U8UBvcCIuwJjFU8Be;wsA(Z zv9~|_Tj$mUHPSQpwef#-fL?QjCJv{k^OI|gNXShVqD!+jOy}b{5!1paID(TyRkWI! zv9Rh)j#QP{b;cl;%|#;S%S%tbUSL06A*2p z5nSMb&XEdU8o%X(XXP>nHptqSiItdMb%+3D? zjX`q0UfsQC?)O!d#fv)UMj3bT-t+qUy2w_R#jrd z7^x=)J%@7wmB6$U@utNNj}AutiAX)>-(!F!DW03ZNK zL_t&%q*C8I)b(?9sF_Nsb(F`Us?}D&@V@T^^jA?!zx?GdyVPz^e9%UAlY$W25L>RY zMOl{UXL-oLRhAxR=aJKu6rx^5#U@ESu8LFMIail#RUIP1^d3Ga-%&*+4UW-+CVO;P zwG+@!^SW?hNuT??JN__DlFw1lqZ)8Fj4=ZSSTE~`Ba(1gCLbNPgu#F(RcDOhF)cZ) zuUU43J_8=aA)tjEgglfyNCgH2s+Nv-l#`x;tKaY8o$vga#c%(%@BF|ck34#pF>JjT zQwMS#*shW#h@uGneji0qz&SVAx=;~_3OVNxMG+c}##i;<_4x4-k?3x^Nuf1iLh5~Lu8?X!qfb+t#m*i9pJTg4D5+6m}q2*SJZrW@XW;^c{c zmG_GMh73T#aa}8Vqj(KQn$}`tV;#MI58nB(5eigtu%zBAF`0oV`d50r>zzMKM0A~s z%;e6QBMV16M~*H$1_1x;JO0sgo;Y>p8>eRX?Pg1z{d=eW_-f%tOa$>V2+$zL2ml6J*-vmaJ*=0LN9*XFfPO|Y3+tXedp^3dy7q0&)&vNq zBGeGdvIG$5pzG`F$n(6)E*gTlZY1&fwe?qW-GA(U`RTX5gGJ z=p#ptUPdKX6;9JsTdl_FIBAe`q*0Vq4+zNfF4k5rq0wkytJ_6cln@PFI#@$PsHikK z02elxzv5JL@4fd@H_Nmr^1RpEVq&5wX|5mtWbp&bvO0}!p}r>sFW5y&kpi@2Xwz71 zCYE3gjCl0>C1t%i&Tsy1NVbOCvQPLqbE5Us*^~b(fQPo@9tV!N3GBnlnYJhkc&AVy zIOhkP4pj$GO1LBR}U{9B0?szys&VDN2KflK#v|hqF*GtQMc2Pte=0f-z)Ck>YS6a z)315;=Z;S{yIDqrhyWn>iUI;fiK1XrDsbDL7zcM-JbtnV%@w(4h} zT(5rsz*XV^jp*irU~CLh1C=4EtJZ>s5_}oZ5AlF{0(pZZNm-S6;Yheg0XZc-dgPh4 zzI)aO^mqTtW6>Vpo3*~zMA@I1OnA06(KwVg(*4$}KvgakGGj9&lO%{P=0%RJ ztqwLew*UxQ?H0B=eSG4fOW42^{MvC)ej02es0XC<3L&`CBbmO1=We+>*~$Qq7DP)h zZMuacM+IL%!>z)-d2+JFQ?1sjr4&!JzXd1KubC(3ki;<{K?pJ;4+Wz~Da0slno4lb ztuY>a%;9v8LDd04zC=V`&q!0(7D->cmyU-zRq2^eg+%Xo*SoNK@gj?OB7`!cbS1JZ z8^E6dn)ZAr<5mWcQ+anzq`4%ywb_Z zJM_J1NxoQnsK2=WpL_jF{ouwxVvX@cB-2yVXt$;sg%;7NGb{Ta=f8(y?s@Q~Cm}#s z;MBu&HQ0egB#MJm2Cuk1!Qy#`hnGvJb3p0A5)c?if=s~Ear6Al?ELTl$)EliJ@&ar z^f!LveH6zLHaEAh)#*^}I3OaZIW0A;Ru7s6>Y>#zL?Mu}i%XBZLysOkDgf}NpZEza zUHEPyMUm%dHq#)JrV6hmgggX-QVcNt0<`9DCpmci{O%V#?_aEIvb*DTz7Lm_9!ou$ z`!6(e`|krkb!qGan%H6`+kNAs`P7>pFVlSx8Ky&(FeULm)WR;o?_6|>!UJSOdNB;d z43Nh?7vGQB&pjH{`LC964aL%FA>CSI;HVvllIb`SA1aa&;Em{eH};1 znG+AaurHUQtP~bIi$YTdb$K!uS;3>jtNAc*Q2gHH88Bk_WabdYJ@Fa-X;_;JbU)5ZC^c$ii)g(zYj$))~ z1MPNOTJ2`pY$lQY&u1`xs1mj`3Tm^Gw|I5`1oJ`zgS#6i|*Dc zc1_J-&-`8_Y0!nP(Pyn3tb^I4hI%oqZNLy4R2Yk3XrnwcJ%cDt5G4`b^ldkzIn_WZ zDU20ZCgh^XT|fmQqxk4&bNt7@F7V>(8@O{uvGAcjkadEl94bbTGFYGrrme!aI!l*e zYz%87O_CU`RvTHCqbLfLWfQHIB2ANuJY+R85kMYs90y7$F&So%Dk5vZhEZu-5|Jlj z0`HoH9Be_2-V-EIMJ$5AR(wVx zK`V)1sRY*@xH617SP~E{HhK)A7z#zd-w!r!j&RrWo{vKZ4?)Tjd7f8AyBi1-Ld{4c zRbr^27XT~;!9)o*H`np9mwg>x^r9Ex#EBDl@*|(YlS?_&MgSZbfaRh>!zy8L0xoPQ z@D*xBhY%Kv%c6w$ z0;d8hGnB?d*+R%dNnpGuSC$ZXo=E!9*w)Yhu)^~i*{D<#5gSq(WEh2E0t*l^!-U}q ztcgHMAaPXy2~&xi&I`CD1u*R^~!J5h_1Fj0jPI%ScJ}lrVUMGprFrb_%CG1H@1U zgdE5N-gr3nI@ST52_sZ;LGhUguw)P{Kr}eQ4wwb?gsVaZYS&`|v^s7lCMU3e|9%v` zKHxoyEC;L^L1Kqm(V>c_FihkKN?L7c8I)y-G;QL62Oh-Z$B*Oq@kep`^eN2F&O?-f zb5s;2kf5r0tZ-W*14i|EQuNChTmp(1OayBPP#d@noKrC2oFN0=-zBC{Z6*UA1;~BHIf7S+c zWDVd=CEpdAHe-O63A=xuO)yulvL}u33Bu^M`g_%AyVvbvb7KQ}KO5{1(!h>_2O1PL zx1%ge+<4=Sm}t*ndHE7V1x1me*YBa(Y=ma4C;$c4*4EHS+Yo6$R6~YmH^_7*Fo&UP z!fWRp#3?q3&_fAPM(j$&pMD3D)+Cy{Z$xS=Y}|y8Cc*7Z&`oz??b2z8IJ8?W?Ag61 z1Re;RTWfge;RhkUz}fQ`AtKl{J%cok(eL*$F)@joZ@M)kiaf{q)&{by2Lj=VCypbE z8FMqcFgH7kcDs#6qk&edg*c7}ziN!zx#7RdIR|Sj3^9!Lc%nCrCx-HC3a=m~id3I5 zor7m@4c%Eqsi`W>Rk@eJ2sNA`LWKuZjTE3#R+XT@29GxwFbEMqgl@MRj$Ht@QXIvI zzBDVtaM-D=dw#N-rG;0Z-|xZN7%38n7f?iiGQ1KD zRjNa5Dx{_|Ct`y*BE*Q0NeNeW!Ds&h2IrBTnMY&Kex#Grkdk0b6X~u)NFqkoS;5|Y zyYa2x@@mY?Oybn3Q~2;l{s!;+jo(5P1;52S&k@@QGt*Nzd;T0+6SFvY=z2Wwj$3i^ z)Ny?HBmWhy=%CZ@qu1-ecN{j>*Kz*BBHFDsCMPE`H8mA}Z@1BGHir$OwXoI>$Vr73 zh@u3PIfyzCDVPkvf`;^P16I!*Xz?#iCE40o2Xs+naKzwc%72A(+nIC^5yGYR$|d=Bzm&xQ22 z(2FKuVL_8OqJ7gOyw8Ai8qYm=JvzN*px6f(f-=F^++y`XAhJdlw19ztoCll(iY*8+ z1WkY=Y;APl#h}rcgqIx7D_YGKe7*tiJ?7_TF~55jXU?3$>dGS0)&#sL8cB>^ua6{R z^m;4kWq_wXW_Jl9YY^E8d-v?ess1Ub_J%A&RrIqA&U^H-J~lgBnC?y^o1Vr*d!j;7 zA~fvfAtexiJU||0(XW;R2+Yv76n;Wa{l?!vl>gkD-|!>lSO+wdf}w~g>>7iDwuTBI ztl-LMT~Y0sX@w=%YNF~PD52_M3=cB0cdp9l0x*FM2Z>!{MoI+2WNQMAMiaXGR`}AjeJ@oRAVltH6^3EeYiF}-Iup7`*S=sxf$uD|CyAwHZ2 zqYJt911BQHhG0bjUx4xrM3w=kVDTU+k@q^`q)1v2C-7o0H#?7{5n-#dh1uC@M3I4L ziRG0gcn=&pco2ZXm5kCPfC9ZfOK z7th`rniz%{DKm^1f(CaWwl+-D5Sl~{%>1yxS(O^37Ty$CKJx_h!o!H77;|%TNYfM> z8yo0m8H9kC7={cin~*t2B(RjigGn~eAp7tKkv}*I=ej_VVQq5@=hri|_dO3wk%z9F zz}ES5SW9-JGx1z>I}S6Q9$M{*V0FO&fuUp}L}2n@BB&4aOOzS3w+hi54HF}ZV>s_X zl%wndv9T~VMOk<_Rv`13oSueF6F5}>28I+$g5^ug$ny;Ib9>;_K~caOf?!O)Z#L>8naHj-H^ z5rahm56+~B^M3lWxBcQD{PuIFe|7PH{=VPUu?^@33;IE@9Hpo-38hJ}WK*&M9wP=5 z2f;w0%FT))L|8m^5*I)6?+~wl2(~>7FCL2*7qPs&jFr`uP&e0t9z-asrl&?c`dJ_Q z_wR?b25m0USw4Y{eisvyGni<%u)8R+aqh1$)37+dco7#DFQMCi40I#IH?9xnI~o{k z9NC~SL;|Ts^H4=EFOjGRB!jw5s5&4r05j%y?ZW2f7V@kIVnS^6e22`o8eC@KOf!Ko*qgqI!v!#_IL`n8j*um1ty zcNfPtpi4DSA}PfO+19G&Rn-8+v4QgnCIQHRtPNN)3oe~s#`(YaB}5lKh>{~ne+v&k z@@bqqcN+6^d$4EE9;~jeB96lGNnAmHH2nYAvu6(`Cnr&sIUauGVVqxFM3N+EBrO~~ za2@96rm@;3Tv+a*(=B0b1Kf;36SI&khc63=dJr+33cLWN2nY;OuKtY*@&zD$z;z%Z zC`Hg<6C7`?0XaplzXfGRE8Q31L{St)0b>oaejlw?8_i}5OG`^tlgkkAP~wWk2`=@);0LC|9O3-p>AG+O8xLa%tE+36o}NZo7NPcK1Lr&p8^lpMkRcEWY-m*n5rvZy z&gaOBK9bbH+9Ys6*uWSI6+uQBtZATW((pXU2jEn^5(+R17|MC9Yv?eB7AGnoGitA1 z3BwG87I`>Sfr5=eYzPPTPNCIk)}*z8P6x9YxHi??3^oQ{0%}r<$4r`_(P-e@`ExjX z?i^n7l9yt3b`CDjVGQHun{UErkADtxvvZi3Xrr^V3TqanuAiahU!6Qaz~YPAqWQAKhp zFWcmfV_ThKCN!F^Kv6J6Jep|>?N&Q%1ToNPHej6@k=hkV@XJ#N<+^uYSU5soZ1d;Z zq>mn6pig;&Lac)$FCuCH2$+QxtQ-~KF0J9+q!KJ@|aclTV(8~=Fa-1%=_yd>O> z_mMc6L|ASqAbf!}QcNHr6+h=LJ+1B^$iIV-amw z?A|?z{qQKOVxufe2rJYOyz>A9Bm_eYF$Nm7!ighY5C!I-^Ij>>6(%2)NSFC7R;83N zH7CVf#!FGCRW-b3p&xp?1whw1+NkBw)BUijDlN{_zoj#KaX<}Z>xl{1)@e6ANMi^az*EJAHzhukFxPKKz|cX3TQfo zV(K7hY8Ls%Q!xEY$TruowR{514y|SjNs?f5a})D(^V#m*^MCG~`@Z@4-P&w6>BPw= zao3%9Ax_ePzQ@YS3U0dbCOq}jX_T(oCNF`BXcE+%MY8u+m?RF=O6EYvRKd{aJw%g< z47@SW#w3g~ym25Zp)fk#ZmvxpxZzTgPR2!9HeKl=?|oDhg#iF-EfH~ph!I5*(li-l z*C>j>9PBj)SzIbfVG(%o;RUL=7hKT+YQJywVY$IMU-4)RL>05M`|zR{ehrMzF)=ZX z&wTDUPMj5qiOMMTJ;e(#1Z#knfG%uiQLacF?Es%+U4aW?zH1{@@XEe)A#! z@JD^Kl^9DkM5ILBtOfhUxxE^|& z9o+r=JFv30gpYsxZ;<5~4(*wTot;oMfYl7vt?Rs=TYt{<;?M1Fn)kPgWw&tuF&*oG zzW-QgAm01J^Goqle~_2G9(f`aF9f#e6Cgp2`u7?5C5(A=-zwpr7X)y6==6xu*O0(;M8#^qM|IQ*=%VNCsdXNO6O5V5xn<- z#>KRaeoEz0x;@KxAPkLS&O#OhrYM3Rj-Wx|{2+ET>FBJOBl1|B<{b zaQwuRh|&c2-18z7{Tx}L*z8@xu4V(g>uyNg1X(~RF#kvaAq$xLXE|6zh+~H!nUHEr zJdu({5H{lc-pTR{?%@3`F1JtnIKS_{`@$H;cy6aDHZGpP;vaq#`wkuex29o?MUm&| z%Pc;3?vL@p8xP|7Hy%QVGW_2UJSLMXd(TYI%>Fn~T-ZHfHedLH_Nld%E%()8&evo> z*KI@`#a$6mRppSGWH8+mQDpr-%Ce|7MQ9+vh{`Oerjgc|E7xAugW#s{B@y=OAI`Gu zl4Ub#j6snX5EaU{GHh*aVe#SxY;0`c=9_QA&;R1P5yuhU|Nal)r+@lqU|PGe8SR7S zPP5G(al|SD0W&-SQKV3Af>lv^k3s|qdurMO4b5blw{$Lbk4`pv|8?KA{=03t#}(WW zTPux5gQ7SAr!7Qj8&TRq6vcpvpl3dZ-X%dfaSzM^M!B^Kcj`}2@~wqtbDzEEp1Yr7 zf~+ie_2|+2uZn2dvp%4;y4l8I#ym0SiW%l|oQCdi2;; z)v>;IGIShm5;LHBnfmC!@wBFXQovO$6s%zyMzuRVhB05+1XWEUgOyaDHLL4qnVi-^$l^fa!&{(2}BZ}{ebD8hDb-$|V z_dL<+M0@JD(?;vPmsZy&y1lMV(s@=SX}8-+`W|VinD2Kt?r>6UT(@uT;oGm9_{*b5 z-nf2v;mEezVnk8m8flC;iK{=yXeKd;0K*Y%WKk3);xs}lP0;Ok;d~e1aMF?P=tF&2jz0K%AN;E~zVRm> z`ozaS^(!~raNYMj`Q(Z3Szq7SqpDb1T7vfu(=#)enwrMOW)~al8`|x7Iyjq^F=zkR z?n5{HdtupZH(M7v-SX1yH}4~3EL3wCc1V(WBoX6lpEs```im#b?YCSn>YP6O#3}&b z*fU&vemNHtBN}94s+#6N<|htVxA!hgN~5^iBCk(5f^LJO&o$8$9y@kSiO7epO=&MI8>8#^!vLpRMhK!p0MKeRyDxdkiyt_5?%c!s_V4=-Cr_OE z(WRxO7Z*h_+t0G3xOfpsl6F*eeR67Q^#%L(txehf-n7~Lg@t2p`(%ws96fsMi;5>N zEG+nMf6Z(4zxOE^6r>n&6jfX3CIl^o@p)t{3JNScJ0N?FOfGdNpudcp9d388+kH_p zZIJg~Rh4QbAZr=k7hq;g&rGBA9?p5N5Ws{85sF?u2;jk%cg1gD4iB(968IX{v!SZ; z)9-k@E-WmRM~@zT6u|3$>$iT>Jo)5_i99bNK+$fu`)_{pn>&LO<=9&r3y1IRe+fk5 z%Rw!+uTL5IJ)Bm6QNR)pUV1`pgB}^pyFS(E!)?)KHCHO z-h1z*x4!kQsvu;AQ^QOuqFk}boD0jd&N~z?$NI(=W_LvdLqZ^w&Kd86R$F)=f!rR) z$$&;VC`FjU?q6W5UUq#cUz7Xqzh9rN_xLi44+5+iQc5pFlZc=c4C{?Dh|Iv2A`Go| z0{Tl{Z++`q6#(wM^Ui;}fB%6$URhq5A>!0nJDp|OM3EP5VzxzIG>f7zd-om4=I7^E z+U@4!Ah-t}c<_zf=RFa}22dDS7_u30?QB5>YS8$&c`%6Dq$ui#o(SfDRt z9Y#c-*Hj%oe3*!s&!1l;YZ-<)-xPgXK84pvTx)GaU)vprwX{%r)L4H|Vy$KX zT3Zoa1#_;Qfd0I^qd^auH5hB^i0&~16>O(aIB}3b^c<+&W4oTx-w~Lj#+9wHU}Z3Z zS0R`H7*wkBL#5VA)Y35dDB^9oPh%U`^DY`S%}f5yH@N+RZrK}95rXAI`y^%?;P8JW@cq#Wil|vP%St!%HOjv6Z4fjkdvG{E|(->oWDTs^`S!K4D z`G(XDP5rxw1kdV92FnU$@m@ZvAyeQevwJI3`hf*P;W zdiphVywWSuQ*iYZ3KnImI+g(~UI+>puGy{}$Xf6SLToifSB8iYhsmmQZd>lzST}`1 z?pU;jqecU{7GDYOP6`;Ubmy>*72)5xA}wI?RNtH0?oQ$TP=sGaFpA%acoy%mgK})c zGa`c0m*`rCoPno9vTt?h4O9(<+$nrEuw>f z9GHx7%kgWhfB)AbO=-}`RoMrRn9mgsK#*B z>aw67Je;8HNyPBpkL7)%!Jcbp-unuJ9%9e+pGQlUoq)c|N2qFT137F62kM*^ z+7bM)qiT9cgK_Z}vRzO4{n`0-SuVRA(V~hY0Vyd+8hR|Id)S)5tmqB{o ziy5yFu3qZFu%xbr6|OSl9PVBY?cYZ3nO^pTc53LW^frf0ilCE3pD#u;I{K~~9S#kU zx#C%0>&@WcqdGg+7&AfOrG%Fd@Khy(S{pE|uNhJyY{$TGtV@n0NxU`If1&JsB@$BS znCgMnx*TIwOV@lk6&)cYNfoF=m~Ke&!rVZ>dFp~5Ijg%=6d8Wl_(riB6x7N|Qi z23Nm^Hi32LWsp_stTSYeRlEi7A>v7)y0Nx)Xl-@1H*QTK5O^Awm1N+W*FW3@uNvw4 zI|xuF-WCSTn2)|mr50cm3Elb?Dx}p}TaUoWd%^m~I-fmzwmCL;>56_r2S%hc&@NGp z!gp4-a9k&a0bOx>7JbH^%c1Ht%maqai*s(sMX0i~!osUTDGD*%GI8{rF}zlOB_pC8w%t{23XS36=o>?rE@T|Nyh*IAD@2siG__OH)4Qr3 zH2R{42=_x2;|k$;!m!o`>9!F4$DW*23lRj<%gf=qz=i(2QiSq6N2k+i zz2OaS;CH|K-JciGM~)mJRV7u`D@7cGY7-4ys;-QiBS((>ox`>V`p^^vNHO0+l%My|O&G-G5Wrb&EBuT3MEE5p{Thh(WrZ5xYIMsfyFGW%4d*1V2 z`q3YKt&F??X0)!q@+GKVj^ROb6<>-pO)v!HL%;?@`%rq69w8eK+mjzi0E4pZw&>*S_wx(R+XS zz2)rOY&1DBJ!LqW64hPKx!YCnVkN!PvN<5mm8$S%SvnOxqpE!obghlf5|Lvzd73sa zCP}jwS)2XlumAQEfILa8&x&3KC7(1VwAbtQM8r3nsrGukd~JQrB}t^rEdTAnkLADm z(O&~T^Lg_N`tI-lE;#Q%LO8JhAX#G}BKppEzDwWombcK`-uAZ3{?5X}!ev_7qDsf! z@eUMg>y7H^%U6jUr4>PovlGqucAjv|5mABwjg_Mb_G#41G1Pq9`ywKTq?s zd$6&wiSvt#*xcN#Tw?>QC*o1M0xF8x*P%o7XWZ45kbrF^8teY|J!@>Aj`7yKJfRQ<=*?& z+-mJwda0&qs_AZ42^f$^W@J1PBhAP&6N+)fOoS%Fn($bR9WsW7?uG^d)C6jCq| zvMmodWJ1OW+-T&GHOLGyTSM=ARo7mXd**xj-d)b}%^&C7`)+0x4uomUQa2)_s{qLl;Dq%5ngG)=&a@y>W>q_pxn(PlgzZ&X#~j5X8uy!W07;Qil!=ijxz z`5XVJ;ZR*eCy)W)o`3Kz2LgKz9fh?P)>;4rMH)-{_F-#l3(Lz_AP*ge6hY2F2ni`A zE2Xao^h=$nvn+#>5=#pU=ykeSUR}ZR$_k7z5Q0I%!+Qf}#(2Dge469%!NbYPm*4S5 zEt5N)ab3u0z#7dFDR7TC){jI&a*aZp)j9w>hLDmOo;-)egAd`8h2;eSO!J}fDgmNb z5)q7bXp~z#V97=2P|vP%%wP+k%uT@{DwRk|IA&8=mn7XhNxGidm-BN=d6FbnN?HDk zPrhjtpgij+%k0>aP^{~8ikl)0V$~tu{^xugp?$eBoZXWjK4(`C@{Dd zKd~W>?)hAuxf}M!HF%AUV0ircn)=izG4oX)!Z|0{vxxQ?;e#Oo7PZ^1_uR;xftkE> z!aEkuSXmX7E{mxyiXEL7JIQ!FN~Y6kVk#3n(+L4O8pX{?2xyg{*Xu!R4XH$fQnc1C zYOQWC#x`Cv*E96DaYfU10>BSE@Bp{F{f|BPv7wUsCN0%%V0u|um9N+-cTP-4`777m zhEPg@nV}O2*iiqH5?}&!q9H09=F)s+a~n`K`&qS2JGB&zd~EK5t$3W`Pz@F&5a2?y=bZ&yU~xlwNDWA#0JbQ~9A#zT zyoUo|1R#w+>>EcCPx^fX`&z^xZw%HE)Mw25*792Z%3};?^v1zK~ z;)RrwZgqL}tp^Y6d(}rD{OHB|?z>NZ@Pi+8wa4W3fWF4{E5Gt9gUgpMznzHg0`nUV z?%#hq37X&D+LBqKg^)s2g>l_M7dw+3DvOfYhZL1p1v&D7k5Ve^10>Qis>*>Vm`f@l zp_M|Kq|iEcx|Hq3n3_$)~V086!z_b0Y#EXB_~NCvLqp+1S{);p<<2*u4AQ_xNX?KBeCM?)Pl` z!+-c;b3LHXmveJN6nR0O6|5(W zCnE@AB+;{)KoBze6S9D}4q8eiN&-$I)e7A#MK?{LC?NDi^kbQ+l2UBdxK$p+W8a5N z=`k~`be^x-%v`IU4w?GuU0zdB#~-iajm5-p_1!~&lwn-xYz0%5DDoWR@dy_$U%(5` zzkqY6PhqmP4IwjDI$Z-x-m;GnIwV8tLUAhg=W{$W5II&?_f zbmPq*`o=fDfkz*G^;JE`VpQ2lSq6et!P!t6%-!?NdcvF_+ApxxRW3xAcSfH91PfqEoP45;B7g*sMN&RT~@TFAT!dz z-eGZEH@jGFNGYL2XaLJ%iuKVZHa0e}zP^U#%a?HW+&OHmu0n}uy&)v#mi9x*l#F8{ zt*V8?H$1;@Y4Kqxg?#eKr~XH!g(MQ9EGu+7fRqBxIh;KC3M8q-*S`EP1c@Nn$N>|r zwWiN~?sK8LD4*!gdFPzIUUFRH`p}0y0L9K7#DR3L06;=ZiOf4x5MfMK0n6v6SUGGi_hHpvX@OZHdcS4tn!~Hk=2tFIA@#s zzSHfZ+f9IIxD8OCkO;%Ds1IBbdo>-aO^-3kS@+=$w3pt zb6`A6K}ac-5q5hMyuHzQU_17aQzrnaqhzm#snzX;fN$gyG<1?8O;ZS^kfsSb-7eBJ zMV4mhbh_yGy6AR#==OTZvM!|7P)QQ(AXIR^B?5>gKoal{<#dEAYiqcC{sPXPKaaJw z6->sXcqao|Dkzq4TEi2Nq#8^Tmcs~SV`B^Jn^S-L$-}3Q-E!;in_~OI8{YVa{OHjm z{QJNE`{dkgQev$|RT(f7tTiA|q+@wF4kw_;%dZn>&BcJ@-s%H{k05 z{jKNE+&Y0@1n~Fnz4zXmu3TQdZF6($)}6`ZnDG4bn;Yx52{Gi=)m743hYXk!y4?i% zxPUPgNGlj7I1gB3Ap{|hO`ld8-dH&AAO%52tv72dlnt&>0v^(Z@2i!Bz#i-@Y&k`> z?Z8ZtJ;NaK4Fb@`rco*dl>}=!KtLrKbebZ|G9;gp=S+ar*P4WCl* z92Xr*f|CrAB_uPZ#TdppO!FzmJ2|Sdg!2x@gL=akUnMa4$PEV%UfS4LKl9bEeDz7C zRK`u85d6HWs*DR3gDg#(c{_XGp!$Un==b|IFLSnBie8-GO<3uV%%*T|Ztm=_{_3xu z0q}2s^EZA&f8{@X^~Ot=F5J4bw8RW2M>{zZmEhp~Je1pkDONGb0mBldrZ)Dq0Gy;L1UDr0Q(@(B01Fa!VAFruziCA zQ2>%~qG-w9hopsFl-{XQns zZ8&G)8dgh~97#d&?81I0jz^W0_WB(9JG|cc&UZ2Z+;`u7;?${A5E5c@bInhtV=*;T zq-huXj=loh$pPdQ!R|bRo%|e#L%HD{8bLy~20pJq;~Yd1%pSVK5Yht}z>dbC=>;4* zd>Eaqi|ws#%*`)AN`bO+DDoW4J~WU@gi&Cc24BR;pmr{FvDStYz&IPmW7a}?hV&lO zGKY8E(4k~Cg-%i=X%D*BL#LCWC`xRsZy}%ND9QqPvD0iGNko4^sl%z^Js=2`cT|l> zD2g1;dA8Ql@#8lqhi^FS7!KY$W)3R-#sqMZa~2DW3s_#c7=M2UtyQR^$wPo6A2BYA zJfo_@UHaX`N&mk@3tW1lQG8QF_!y>A^SHVr^|4x z;mZl&Z6h@3oddD0aTEyjErQvZqFZi3?SDDw$g44V>_yyg!y(*s(=GVpKmKE2 z*4NiUu#$ij8cM0)D=q|-QqWqX(@7zPgibW16i5<<(hEoiI#L2`KydKWG-OC`;XR|H zyBH3KU@+F#*D#$<;XOl&6e>2U#;Pz!lQNF`ETqs7WMQp?(hAO6Xq`gJ1oLw}8Vm#6Qr#Tk5;Jg;vvDFwSXx}dO4o5N7#OZb-dD0X%Bl8_nqSNV8uh%_s z`O2kV7NipIxd~`z4Lo@)E$xR+6waPG14bxCl#)^LKLVqeA>u#0V`!bwzj^4h`t3jS zv*tSq-fM1Ge~1ED#Cfd;2t`p)k(V^TFgLfpzW$a+AAR&)S|y|T`Gv(iFAk2!<9*A^ zS31r)D5XhCiSc-ZG|RBDzK&kM2WE%0^))OlE@C_$H zufSe>1WF4?nV_mF=tP5k1?Rcp{7EUHRf;6Zkfvd}k|YU|Bte=cP%4}jN-5L=mNI5> zAp|pfY;A4gi6@>Qok*TccBp3aRF%Wq-uBaY&1>$&M<4u6bg~Y*odJ5i9=5l)u(W>> zN(xlQBGDZP5-g>{!otFtm)&;aoYu+6I~w(R-4Wn7Alz1(r0YddY-L%t;=O zRu4=j)BV%w8ArN7du^A#MYbRuNL6}S@SX*6#kP_2;f?ltW$|@Y*e*}}!7~_0`pZG7|icYVCv**r0DHXv> z9v}}P3FGk;U;Em_SXx?wRs!I#$rQ0~@ZM2TOlUHh^3Ki}d7g9qx)BMQ>|A>D4`dD9 z3Shy^z77BUt^e~MC+i#AH;%{SlcUk-j;Eh`_Las~$0w7?p`s{`0wNvshg?qxs;ctL zj4aE@S}W>K+&Kqp9jtXkM98uX+6g%Ck#$nABV<_z#uyl5kmxMbu{y0wU1*)|3c{%0 zhD6CAjTM8}TH8_(!Zk6{*Sf;@S%3kh5-^8suarhvxPZ+N8cKvIh?F5%tEz%hp|%a# zcxEWd_2`l9RVTv?tp|WYFcJ~0VU%SBDN}e~VKNz^C?=SjKZ?6f97U0DVteZFrZ>F_ z7cX7Jl`B_f@&;_f;{XsOK|+EZP?x|PE%M@ z{sC}2W~r(wG1?xh_rB-eWo$pR%+cJbS~! zgPbctQDmD0;n0CYxN`Xlm?*}#@1s;X)-B@pBuy>67vNO?9&Hp~WA=l#v<9Qo=^)Ee2mzE;f$h-<+uK+0=+ik)-g+y#^ZP)~ z;Lrc;&rlYjmr!aC0+4{RC@`H$WI9E+(}UC+OoWY%&0QFk#_+V&;3Yco-**B1zW2S4 z>ayW|?|&a@rE%}Q_i~!1swk>nRhhwPw6k9bx`Wxj(R=^e%a>M8R>q{vEaJrgv1A?S zblwV*9^U&Il+k+#l9=oFnm4rH>7(E6qt_ckNsXPI9c*uJKqmt8gCRP-KB~%KeSICh zUI#?L*47r1B*DhU2J$?IkU+2B0T{@O9Qkw#YXf7$M^?D^wwb|RD+}+%Ft>xL4tgO3 zj4>$75`yZcjN+C}G@dWcS=15?c`<+~=~dLP5q6(kwG=NvOhlAs*|)9oN-t#q*R^k(D*y5S>$m0V%I5x!_3fjTvB#7W zC%xm747{Q$s}s)Iq4N*$ORvm-C4PA{7{3Ja5YP&t+rY>yzTmWX3yqh85Bth zwI4c3BuNIP0$NW>1+BwlZEtU5V`Bpg3ya9I4E=sTY!CrRAs~FORJ0%acoIM zm>V9r@|n+m^6-C_fW8(B;lm&P@R7-6@`|eTuL8uKMV{X}8jtoRi9R5Nm}4di)H;rk zNaA4JF*CPo0wQPF>8hZrwU+5ym>9L+@cL^*VSX;~>Kh<@gvYM+P3jC=mSw=a=wx>(5&c(W z{M{V;hR(M8<_`Z}T0C;@4?q3SZv37H^u71qCpI>=v*Dn(Pbzgc5xr?^WApXJG`~Gb zlb&al0+a}$hyzfT;>i;R@V*fOipP@Qt)s?1N=vnlOY#j2seZwVe2VqeE09t(gZ<{| za$T=+7_|x6WHav>&p!VQc4CHh);GZYngb|=06~Ew*a$$QV9!zJF4WWY+&OakIQU-I zH)5S#XW1~GNdmiS$~rsmok-L(Y7yBq!Tuqtf!2*6IF=$Jg8S<%9C(66VSCul$aSFC z&#UbYThG<&8)S9@)%CAz5fNr?SOOG($JXyJ2&kosnZZ)*!i3lTk062hxdY4p`j0+w z=zCs6udb}Vx-#zGvMAr^y*~g1uuY=7P*BbZL7qX*d7_}K63LpO#9+~;_@mykf<)Y& z)h`6vb*XG+z1oQhU>~?m(KI0%_{JL}nE3)saLz+A;LNO%tDC<%Ky>RU4gsxaS=0Fl zO?Ci@T=G3_3g7?!_tWLeD~GnXwm;|`{+d#W05eM|d1tgmrYb2*Get^) z;t>^&$?*3O+pe;U-*av>S<5 znNl}mw5w#OU9p$}r6iIh2>|{y3wR8rA*G(N3AtqgRg+TVDj3ZYgW~h|V0ODE4YQxe zEu4f0cKMkQq)6imTLfT`kj-mJ@n%4WnxsH2MNlavI^9lFcT>x6u>G9dL0&6M0Wsmo zc+Lb-hnXL^=jOeszkhZ^pmhS*ws|f7BmwNa`kn>!efQljwl+s;RoZuWC*PXrghGa3 zF`4Y(`BP6rlR}y$u+y#Q5P`nQlx=wwiNVhzG4VgswKCsS%bF) z%9#Nv!^tOwj5rhc5XiS8mT65=07aXMS!qZh$e3}N2wYQzKwzdOF)?OY zUqtphw@#&yXapEmd`|-U{`>DIr4-7l`k8!M{5_?#3TsV}^B(8UpMk3!UVTI%k;|A? z8OFKC3iV(OK+`R3?2O<|4iX9k6+ktlAhm?j1VtHyP9del(&BzB%lZCzOX7B5Ind3Z{8uqu+8vR~|UayD#ppQg#p>+bKW=FjD{;CmO z`24KquSH_oKfm20G+e<}leu@)SBoXhZXj6;*ET#x(K7dG|4vO~CB))_4{O{Ow|wc#8Fg8>%i_hC33HmJ1F&C{&JX<<9+ z;F$$?1EQ++=uVC>Na!OYf;+s~y=928fANwA^!@kWPm{@{v$eVPcLa%_)LIJx zB|siKJ7YZm!c)ll0`Gjy6fZvv7BtT&6EWL30G^(n? zkDok(g_m`)+D(w2ehM$*jrfz3e-%d)k7upGfs1GHzx}&UgU((;UnUqocLuinIUIS} zF}U>+vd0WQb?(pcm7A7v%fbmjsAdiwnxCsz$Mv7q7*v%(RLu{8V^R%Bn(DjP;mN3RNJxSQA zQe3_JUB63R&{pHQ!*PO?t82^bm7u@Tv@$<;f=TA&))FY@d@hVma)aboe8jg0>$J- z9PSeO1C6_v{|Fl=)-YXIz+ieATl-&uLv%YP`4-g2M|kSWDLi!N6F73{W>CzMwQ)Mg z&n5?`UDw)!euqR!DA|odE}{kS3FU@EY|xk?5p*!5RM2S$Ns<8^sr@lC)-2|_eyrd8 zb`860Y67=wYSGTB)M(+MIp*uE9i}$2k?N|qkwh~yP^C0_-9FBodjVILFUFt>Crz{L ziShwW8O$O0f9kLQ*Esd!)7Y8(1=7v*8jx0M7Al^?*poTD%k}Tf&WFrS1VcfCgT6(; zwnwCb1USC_Wm$M*OD>916nRe8S}-J}5aU~JxoP#yZ+^4=>es$<4H&?8F`(~%-~FUW z?Z0wy`9UetTLo0D;6vM6TX^==v*`9asNVQj@N=(yJw$2owT%^=**K2>>5e-vKL>pB z;VDj5e+;_&&+zzb4`5QRW9yQ|4$flNm|f2kSjzRRF=E;gM27s031K?ADs2SzWjnKfDLu+(wP< z#ZA=BU_IBaH*@teDeOW-)7XmEW-*i};jy~iE_No{xODLx0Jn?6)efehux}z<)*|a9 z`1EJ~H6DNF2@D4*taXt-)+QUwg3^Io0+1Z5+x8NqHFj(@#-V(gHH=bw@rukf_&;M*2YZK zUmpTkTm#cE`+Q)aNIdiGBdAQ(EKTdjs;UZARbe`vM#yW~9Q%R@(wr*wSHnk{B)Ybj>C{@4KSer`eQuO1BwE{ejBMqAVIc)U89OkOC<;!lMT|8zJQ z{8J(EOjT8{mQrR)=|T#*`Ifi*7mwY2_pAP&G)>lTy6GnR!WX{q(q!nWtn~Kk#?LW} zzbQy6W|sB1=h^2T!(_aM?8f65-t`(B>LeI>4{S`3xRanP!)Kl_==m4%*4zFie7X%Q z=TQ{}&Tfn$4`@8K{uP|qcL(sLF4WUYQ1cp3JoyA}Jbq)dh9x3cH=7#=V0)Nd3MoOc zbRbFal*Bu>R@MyoVdnzMP9c815P%@~_AH^brNW+OtS&e99);fu61uncon>-vjn&#; z-(HJTQ|ps(kU}9%I#^!5fbn>z!CWviMx#+P;i&~}f}S<+at&D8cQ7nfK*;maE}V9K z)s3oX5q2GXz{Cg&*|}90*Ry11f^*K7#<;SYPS@7fR*f;|v{sMz`~9yDhl9uZgZ@gd z*B>t|%varRXJs;(`~iUdgTWv_cI^1YyY9T}FHYQg;`Apz@d+ZyNn~n%o2I`u_ooW#5P|O0d|e~G2+&&9*6B+ z%+yWeY~~%Bw4$8g(&dXy<3)ln*_kvvMIpsi$kTc@?7TxJGXTJ7G}^Tk+Fc&)-e96u zK?1`%3&~-P9&Vc)>KtBtxR>nIsc4z)swwm_o?~$`KRXQh8G8eL4Ne;vFgDG zA8elFAO7JVp4qo=-$xG~JSYwvIN;~!=Z%!I{7>19eA@%-zxB6%>7OW-{x4c8Vrw9T zAnyZYkRV{$!CMb67_M@#wuJQ_rZBMO6vkQ@Yf;z|l$20Ppp+A2hZH)U9(>>6+#^?T zOY%A_E$oBw6;ulZR0m_ow5F-cW^aZFRG)glUqdLPU2oX_xiIDggP?*nS@`TM5F*w> z%sRHKvhB4dD|>^vy$!rN*lEv8KLaf7W|+*HMfQX=RXBIyG}hLZn?M|Gk5EK!lSWLv zRsPhcN&EA|;Sigfo6uU%X7>@RK}6_uIzivVg-MBb4lbC+gqyr2cX7)@FB&3Yue=|&N zEtDcipxcdJ8>E5)kn<2SykM0vkV3#3hoUUurWMR3gA?io%nj$^tPe_@acJMn+sU=O zMR3(jR!DB3n!=-5J=Z{)U@=k$mjh&Y!^n#ub{w0QWLK>Lzq=*%3Z3 zcWoKbnyc6TE7u|dFncJikrxxJt*z|(nTjk_+$`3l)4G=4%bK}X>C`+<#Ez$F+CW5I zjYS~=X2WHfyRs~&##Ec$`AbTxN0TJ^ayBFphLB;lDTRX82>^mt5<*ExA<^x1Acp<;B1g0M z=1qw8ay2bf9~%ybEB$_dvbeZd{`PPG z_VzwLm&;f6yW(pr5DjN|L;8?q?M8#XpJ{(EE0 zJ_OWdWIG@*t4TpfF{44n|5G|BWr zU_wR}`hboTN(XC(_<12_a7#eI`<9>zi|9YXEmRz8yR%q_^Df;B@(s@>LPcZ!TCa3f z0Mv)aqdiy;f6X?g*7^uHRybv{EX8Cp#`5xID5dwX#tA}ou(&jboyjE5;|+WsRZ^7< zvR`T1An}@vuXM9yGNy8Qv6B}?IRbDtNs_N8Y4Wu}I(Mqq>s{=0I&0l-w;T)x?&BZ- z`1f59e@6k`>vq@W!GnKLBS<{+{%Jlru(7f6-y}(rIB&shVEr&8i#FEc5gH-suie^7TM&qA4;Drbp zb~;j#lnz>-&EDjDCxCAYF}y>w`mISdY%-xhso@5gST7a1O3%osOBfK(v~ zT)cFyk@T7&@TlQMPNvhKUK&qGQyDBQ%r``Zy)e=|pZcAh9XrjZQzhk^Qu3lsg1C4E3eZoTix*B6U&<0+_t_1Y&Z|k?~d_3ZB&6`fTnSM{{vi+SXf>D!5Q- zp&)!bzD3BB60>bwi0rVD^Xy=(1#AfPhGD_8fD9svstM9sxUd#A4>v$$uaE#y=26PE4NX6K1}k6ZPD1*n6tq?>{;i3?84Gn_HWko0}d#e%#!9 z@4Y`T0sNn|`i>wG27pH%c|e^nC0x+8vSYOw*7CU*-Y`td8Zf<7NiexiU2;~z&sKBlX4V-Gc z{*(we;Eb76`x6F-DiRdM&Iyua17y-#V=@`z#WSZPLj>2<*cc9nyS9^|v9_p8fubl} zRaJ#GW-OIFon_f0OTC3B`@P*>+Yw1 z@~>X_(&Tc($K<4%uWC_fwJ_X!XmStz==7wMEbUI&2)AWUYzrUO$$(Gi- z8V-m4cYf!0ekg+YJ6f#l#dZ9`FZ=@7)7;wHdjHCmWwE`zNkuuWJ+HmBb}P^G(LMLv z)4XWE_=~?t*4n5^oC#)@b&y(Xm2*xy=jy*J=Um3jb%4*;!S20Zu-4X}?>pzxnDG@AsbWcDoz>et#mRtbX@*fA@zjfWIpN{k^>IyYD_x6ooLx2xE+N z&XIFYT5E-KPQ;s_>%AX3=la&#zO{Dfy&qU>ht9cyb8f)QJ@0*wnfu=RzW2Vv%suwp z_w0M|a|sa%=bVVbfr6Qdh=jFP5D^I}gBzA?%LO1dx>u_Y)i%gsj4nZ>uDL*|r#ST~ zK-WO-1!X9M%9O6E%E~!Al2V>YlH}QLxBFDT-+z2K96mi94%g=9hQ-G|_KysH#4h?_ zc_Qz5&wE5wRa8|K8Dq#fN7h<$&XMp15!=UmrXn>pt)?|mGM)mGnME67T7M{&04%PF+5@2xy#atEN#LB1 zLh2sY8e3JSVD{TO5iev}`f!@2kM?@qQ~mznT(8$%@An7Q;lqdBd*AzB#*gSlKT1#b zmw)+}DT>KZt*Rdbq=8Q400075Nkl!b47AVhi{^_6o>8~FJpX)uI<2G-LeI_3 z(`~n%n7rcT?T;TldemKi1+NG6_vCuufd{VYL+YhUTTdcbMc8^)O>x6W+uPgn(4j+{ zH{X2oBi7n(RaF$p5)&d??f3huH{X2o^$dNzDg6GqKJbAL?51-^Yhi0GpFDZe-*U?> zYA_h+wY9Zd0VpZucBhkFDT=cE>}NmgyWK9|fB*g0ryAF<>(}+``gQ&K0lxl!>@`A` T#_|TS00000NkvXXu0mjfE)IFE From dfe47aa4e47d24a2341ba92016dc13f0c1ff7a95 Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Wed, 14 Apr 2021 18:48:48 +0200 Subject: [PATCH 252/285] Delete x1_thumbnail.png --- resources/profiles/Artillery/x1_thumbnail.png | Bin 36381 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 resources/profiles/Artillery/x1_thumbnail.png diff --git a/resources/profiles/Artillery/x1_thumbnail.png b/resources/profiles/Artillery/x1_thumbnail.png deleted file mode 100644 index 4aa3a0dcccebc7aaa852a8a82eabd9940a9915e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36381 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%0D`01xkydjCR>yIBl{-U7p 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 BwlDwy From 1e69e6def14940e044d053d3baa55075302d2f2f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 15 Apr 2021 08:41:20 +0200 Subject: [PATCH 253/285] Follow-up of 526233ca472da33070a0d576fe8144100e02cb43 -> Take in account original instances scale factor --- src/slic3r/GUI/Plater.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index a3d30d5f8d..50adb89e41 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2425,7 +2425,7 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &mode } else if (max_ratio > 5) { const Vec3d inverse = 1.0 / max_ratio * Vec3d::Ones(); - instance->set_scaling_factor(inverse); + instance->set_scaling_factor(inverse.cwiseProduct(instance->get_scaling_factor())); scaled_down = true; } } From 64ec319017258fa99c6abc173c65a5868e5bc18d Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 12 Jan 2021 17:01:14 +0100 Subject: [PATCH 254/285] Fix for arrange crash when geometry has zero length segments fixes #5749 --- src/libnest2d/include/libnest2d/geometry_traits_nfp.hpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libnest2d/include/libnest2d/geometry_traits_nfp.hpp b/src/libnest2d/include/libnest2d/geometry_traits_nfp.hpp index 29a1ccd047..6b3ff60c18 100644 --- a/src/libnest2d/include/libnest2d/geometry_traits_nfp.hpp +++ b/src/libnest2d/include/libnest2d/geometry_traits_nfp.hpp @@ -220,7 +220,9 @@ inline NfpResult nfpConvexOnly(const RawShape& sh, auto next = std::next(first); while(next != sl::cend(sh)) { - edgelist.emplace_back(*(first), *(next)); + if (pl::magnsq(*next - *first) > 0) + edgelist.emplace_back(*(first), *(next)); + ++first; ++next; } } @@ -230,7 +232,9 @@ inline NfpResult nfpConvexOnly(const RawShape& sh, auto next = std::next(first); while(next != sl::cend(other)) { - edgelist.emplace_back(*(next), *(first)); + if (pl::magnsq(*next - *first) > 0) + edgelist.emplace_back(*(next), *(first)); + ++first; ++next; } } From d5ddf8b00eae98b25106aefdb2fa90a873ee8fd6 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 15 Apr 2021 13:48:20 +0200 Subject: [PATCH 255/285] RemovableManager on OSX: Testing for dictionary values for nullness. Hopefully it fixes Can not start slicer on mac Bigsur #5719 --- src/slic3r/GUI/RemovableDriveManagerMM.mm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/RemovableDriveManagerMM.mm b/src/slic3r/GUI/RemovableDriveManagerMM.mm index 3bd1fba7eb..8542c2509d 100644 --- a/src/slic3r/GUI/RemovableDriveManagerMM.mm +++ b/src/slic3r/GUI/RemovableDriveManagerMM.mm @@ -80,7 +80,9 @@ static void unmount_callback(DADiskRef disk, DADissenterRef dissenter, void *con NSLog(@"-%@",(CFStringRef)deviceModelKey); */ if (mediaEjectableKey != nullptr) { - BOOL op = ejectable && (CFEqual(deviceProtocolName, CFSTR("USB")) || CFEqual(deviceModelKey, CFSTR("SD Card Reader")) || CFEqual(deviceProtocolName, CFSTR("Secure Digital"))); + BOOL op = ejectable && + ( (deviceProtocolName != nullptr && (CFEqual(deviceProtocolName, CFSTR("USB")) || CFEqual(deviceProtocolName, CFSTR("Secure Digital")))) || + (deviceModelKey != nullptr && CFEqual(deviceModelKey, CFSTR("SD Card Reader"))) ); //!CFEqual(deviceModelKey, CFSTR("Disk Image")); if (op) [result addObject:volURL.path]; From 48a93e40fb2499832ea4dd61185c58912f6ef18a Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 15 Apr 2021 16:29:30 +0200 Subject: [PATCH 256/285] After issuing the color change custom G-code, which is most likely just M600, reset the internal retract counter, so that a retract will happen after the firmware returns from M600 to the initial position. Fixes "Blobs on print after manual color change #6362" --- src/libslic3r/GCode.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 0e2b1a57f6..a799408109 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1778,6 +1778,10 @@ namespace ProcessLayer else { gcode += gcodegen.placeholder_parser_process("color_change_gcode", config.color_change_gcode, current_extruder_id); gcode += "\n"; + //FIXME Tell G-code writer that M600 filled the extruder, thus the G-code writer shall reset the extruder to unretracted state after + // return from M600. Thus the G-code generated by the following line is ignored. + // see GH issue #6362 + gcodegen.writer().unretract(); } } else { From 66f6c8c786919958c28345ea10589f673cd8f21c Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 16 Apr 2021 09:48:22 +0200 Subject: [PATCH 257/285] Fixed conversion to utf8 of strings entered using Custom G-code dialog --- src/slic3r/GUI/DoubleSlider.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 29b488b907..84a499d6fd 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -2165,7 +2165,7 @@ static std::string get_custom_code(const std::string& code_in, double height) if (dlg.ShowModal() != wxID_OK) return ""; - value = dlg.GetValue().ToStdString(); + value = into_u8(dlg.GetValue()); valid = GUI::Tab::validate_custom_gcode("Custom G-code", value); } while (!valid); return value; @@ -2173,7 +2173,7 @@ static std::string get_custom_code(const std::string& code_in, double height) if (dlg.ShowModal() != wxID_OK) return ""; - return dlg.GetValue().ToStdString(); + return into_u8(dlg.GetValue()); #endif // ENABLE_VALIDATE_CUSTOM_GCODE } From dabac9275540219a691d0f93ed4340183186405a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 16 Apr 2021 12:49:57 +0200 Subject: [PATCH 258/285] Fixed flickering of 3D scene GUI when the scene's bounding box gets very big --- src/slic3r/GUI/Camera.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index ade1d40437..6c183c3430 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -330,28 +330,28 @@ std::pair Camera::calc_tight_frustrum_zs_around(const BoundingBo far_z += FrustrumZMargin; // ensure min size - if (far_z - near_z < FrustrumMinZRange) - { + if (far_z - near_z < FrustrumMinZRange) { double mid_z = 0.5 * (near_z + far_z); double half_size = 0.5 * FrustrumMinZRange; near_z = mid_z - half_size; far_z = mid_z + half_size; } - if (near_z < FrustrumMinNearZ) - { - float delta = FrustrumMinNearZ - near_z; + if (near_z < FrustrumMinNearZ) { + double delta = FrustrumMinNearZ - near_z; set_distance(m_distance + delta); near_z += delta; far_z += delta; } - else if ((near_z > 2.0 * FrustrumMinNearZ) && (m_distance > DefaultDistance)) - { - float delta = m_distance - DefaultDistance; - set_distance(DefaultDistance); - near_z -= delta; - far_z -= delta; - } +// The following is commented out because it causes flickering of the 3D scene GUI +// when the bounding box of the scene gets large enough +// We need to introduce some smarter code to move the camera back and forth in such case +// else if (near_z > 2.0 * FrustrumMinNearZ && m_distance > DefaultDistance) { +// float delta = m_distance - DefaultDistance; +// set_distance(DefaultDistance); +// near_z -= delta; +// far_z -= delta; +// } return ret; } From 074a44833e0820104212242eeb9aefd72ec1e633 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 16 Apr 2021 13:44:01 +0200 Subject: [PATCH 259/285] Removed mutable members from struct Camera --- src/slic3r/GUI/Camera.cpp | 33 ++++++++++++++++----------------- src/slic3r/GUI/Camera.hpp | 17 ++++++++--------- 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index 6c183c3430..82522b7a33 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -123,7 +123,7 @@ double Camera::get_fov() const void Camera::apply_viewport(int x, int y, unsigned int w, unsigned int h) const { glsafe(::glViewport(0, 0, w, h)); - glsafe(::glGetIntegerv(GL_VIEWPORT, m_viewport.data())); + glsafe(::glGetIntegerv(GL_VIEWPORT, const_cast*>(&m_viewport)->data())); } void Camera::apply_view_matrix() const @@ -139,16 +139,17 @@ void Camera::apply_projection(const BoundingBoxf3& box, double near_z, double fa double h = 0.0; double old_distance = m_distance; - m_frustrum_zs = calc_tight_frustrum_zs_around(box); + std::pair* frustrum_zs = const_cast*>(&m_frustrum_zs); + *frustrum_zs = calc_tight_frustrum_zs_around(box); if (m_distance != old_distance) // the camera has been moved re-apply view matrix apply_view_matrix(); if (near_z > 0.0) - m_frustrum_zs.first = std::max(std::min(m_frustrum_zs.first, near_z), FrustrumMinNearZ); + frustrum_zs->first = std::max(std::min(frustrum_zs->first, near_z), FrustrumMinNearZ); if (far_z > 0.0) - m_frustrum_zs.second = std::max(m_frustrum_zs.second, far_z); + frustrum_zs->second = std::max(frustrum_zs->second, far_z); w = 0.5 * (double)m_viewport[2]; h = 0.5 * (double)m_viewport[3]; @@ -162,16 +163,16 @@ void Camera::apply_projection(const BoundingBoxf3& box, double near_z, double fa default: case Ortho: { - m_gui_scale = 1.0; + *const_cast(&m_gui_scale) = 1.0; break; } case Perspective: { // scale near plane to keep w and h constant on the plane at z = m_distance - double scale = m_frustrum_zs.first / m_distance; + double scale = frustrum_zs->first / m_distance; w *= scale; h *= scale; - m_gui_scale = scale; + *const_cast(&m_gui_scale) = scale; break; } } @@ -184,17 +185,17 @@ void Camera::apply_projection(const BoundingBoxf3& box, double near_z, double fa default: case Ortho: { - glsafe(::glOrtho(-w, w, -h, h, m_frustrum_zs.first, m_frustrum_zs.second)); + glsafe(::glOrtho(-w, w, -h, h, frustrum_zs->first, frustrum_zs->second)); break; } case Perspective: { - glsafe(::glFrustum(-w, w, -h, h, m_frustrum_zs.first, m_frustrum_zs.second)); + glsafe(::glFrustum(-w, w, -h, h, frustrum_zs->first, frustrum_zs->second)); break; } } - glsafe(::glGetDoublev(GL_PROJECTION_MATRIX, m_projection_matrix.data())); + glsafe(::glGetDoublev(GL_PROJECTION_MATRIX, const_cast(&m_projection_matrix)->data())); glsafe(::glMatrixMode(GL_MODELVIEW)); } @@ -202,8 +203,7 @@ void Camera::zoom_to_box(const BoundingBoxf3& box, double margin_factor) { // Calculate the zoom factor needed to adjust the view around the given box. double zoom = calc_zoom_to_bounding_box_factor(box, margin_factor); - if (zoom > 0.0) - { + if (zoom > 0.0) { m_zoom = zoom; // center view around box center set_target(box.center()); @@ -470,7 +470,7 @@ double Camera::calc_zoom_to_volumes_factor(const GLVolumePtrs& volumes, Vec3d& c double dx = margin_factor * (max_x - min_x); double dy = margin_factor * (max_y - min_y); - if ((dx <= 0.0) || (dy <= 0.0)) + if (dx <= 0.0 || dy <= 0.0) return -1.0f; return std::min((double)m_viewport[2] / dx, (double)m_viewport[3] / dy); @@ -478,10 +478,9 @@ double Camera::calc_zoom_to_volumes_factor(const GLVolumePtrs& volumes, Vec3d& c void Camera::set_distance(double distance) const { - if (m_distance != distance) - { - m_view_matrix.translate((distance - m_distance) * get_dir_forward()); - m_distance = distance; + if (m_distance != distance) { + const_cast(&m_view_matrix)->translate((distance - m_distance) * get_dir_forward()); + *const_cast(&m_distance) = distance; } } diff --git a/src/slic3r/GUI/Camera.hpp b/src/slic3r/GUI/Camera.hpp index 91f4661b46..574a6eed76 100644 --- a/src/slic3r/GUI/Camera.hpp +++ b/src/slic3r/GUI/Camera.hpp @@ -35,15 +35,15 @@ private: float m_zenit{ 45.0f }; double m_zoom{ 1.0 }; // Distance between camera position and camera target measured along the camera Z axis - mutable double m_distance{ DefaultDistance }; - mutable double m_gui_scale{ 1.0 }; + double m_distance{ DefaultDistance }; + double m_gui_scale{ 1.0 }; - mutable std::array m_viewport; - mutable Transform3d m_view_matrix{ Transform3d::Identity() }; + std::array m_viewport; + Transform3d m_view_matrix{ Transform3d::Identity() }; // We are calculating the rotation part of the m_view_matrix from m_view_rotation. - mutable Eigen::Quaterniond m_view_rotation{ 1.0, 0.0, 0.0, 0.0 }; - mutable Transform3d m_projection_matrix{ Transform3d::Identity() }; - mutable std::pair m_frustrum_zs; + Eigen::Quaterniond m_view_rotation{ 1.0, 0.0, 0.0, 0.0 }; + Transform3d m_projection_matrix{ Transform3d::Identity() }; + std::pair m_frustrum_zs; BoundingBoxf3 m_scene_box; @@ -119,8 +119,7 @@ public: bool is_looking_downward() const { return get_dir_forward().dot(Vec3d::UnitZ()) < 0.0; } // forces camera right vector to be parallel to XY plane - void recover_from_free_camera() - { + void recover_from_free_camera() { if (std::abs(get_dir_right()(2)) > EPSILON) look_at(get_position(), m_target, Vec3d::UnitZ()); } From a393df59d74dc252e8e977afa2c35875b2fcf02e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 16 Apr 2021 14:05:55 +0200 Subject: [PATCH 260/285] Further refactoring into struct Camera --- src/slic3r/GUI/Camera.cpp | 156 ++++++++++++++++---------------------- src/slic3r/GUI/Camera.hpp | 10 +-- 2 files changed, 69 insertions(+), 97 deletions(-) diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index 82522b7a33..d8b80f1ce8 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -21,12 +21,6 @@ double Camera::FrustrumMinNearZ = 100.0; double Camera::FrustrumZMargin = 10.0; double Camera::MaxFovDeg = 60.0; -Camera::Camera() - : requires_zoom_to_bed(false) -{ - set_default_orientation(); -} - std::string Camera::get_type_as_string() const { switch (m_type) @@ -49,11 +43,6 @@ void Camera::set_type(EType type) } } -void Camera::set_type(const std::string& type) -{ - set_type((type == "1") ? Perspective : Ortho); -} - void Camera::select_next_type() { unsigned char next = (unsigned char)m_type + 1; @@ -65,24 +54,18 @@ void Camera::select_next_type() void Camera::set_target(const Vec3d& target) { - Vec3d new_target = validate_target(target); - Vec3d new_displacement = new_target - m_target; - if (!new_displacement.isApprox(Vec3d::Zero())) - { + const Vec3d new_target = validate_target(target); + const Vec3d new_displacement = new_target - m_target; + if (!new_displacement.isApprox(Vec3d::Zero())) { m_target = new_target; m_view_matrix.translate(-new_displacement); } } -void Camera::update_zoom(double delta_zoom) -{ - set_zoom(m_zoom / (1.0 - std::max(std::min(delta_zoom, 4.0), -4.0) * 0.1)); -} - void Camera::set_zoom(double zoom) { // Don't allow to zoom too far outside the scene. - double zoom_min = min_zoom(); + const double zoom_min = min_zoom(); if (zoom_min > 0.0) zoom = std::max(zoom, zoom_min); @@ -138,7 +121,7 @@ void Camera::apply_projection(const BoundingBoxf3& box, double near_z, double fa double w = 0.0; double h = 0.0; - double old_distance = m_distance; + const double old_distance = m_distance; std::pair* frustrum_zs = const_cast*>(&m_frustrum_zs); *frustrum_zs = calc_tight_frustrum_zs_around(box); if (m_distance != old_distance) @@ -154,7 +137,7 @@ void Camera::apply_projection(const BoundingBoxf3& box, double near_z, double fa w = 0.5 * (double)m_viewport[2]; h = 0.5 * (double)m_viewport[3]; - double inv_zoom = get_inv_zoom(); + const double inv_zoom = get_inv_zoom(); w *= inv_zoom; h *= inv_zoom; @@ -169,7 +152,7 @@ void Camera::apply_projection(const BoundingBoxf3& box, double near_z, double fa case Perspective: { // scale near plane to keep w and h constant on the plane at z = m_distance - double scale = frustrum_zs->first / m_distance; + const double scale = frustrum_zs->first / m_distance; w *= scale; h *= scale; *const_cast(&m_gui_scale) = scale; @@ -202,7 +185,7 @@ void Camera::apply_projection(const BoundingBoxf3& box, double near_z, double fa void Camera::zoom_to_box(const BoundingBoxf3& box, double margin_factor) { // Calculate the zoom factor needed to adjust the view around the given box. - double zoom = calc_zoom_to_bounding_box_factor(box, margin_factor); + const double zoom = calc_zoom_to_bounding_box_factor(box, margin_factor); if (zoom > 0.0) { m_zoom = zoom; // center view around box center @@ -213,9 +196,8 @@ void Camera::zoom_to_box(const BoundingBoxf3& box, double margin_factor) void Camera::zoom_to_volumes(const GLVolumePtrs& volumes, double margin_factor) { Vec3d center; - double zoom = calc_zoom_to_volumes_factor(volumes, center, margin_factor); - if (zoom > 0.0) - { + const double zoom = calc_zoom_to_volumes_factor(volumes, center, margin_factor); + if (zoom > 0.0) { m_zoom = zoom; // center view around the calculated center set_target(center); @@ -289,8 +271,8 @@ void Camera::rotate_on_sphere(double delta_azimut_rad, double delta_zenit_rad, b } } - Vec3d translation = m_view_matrix.translation() + m_view_rotation * m_target; - auto rot_z = Eigen::AngleAxisd(delta_azimut_rad, Vec3d::UnitZ()); + const Vec3d translation = m_view_matrix.translation() + m_view_rotation * m_target; + const auto rot_z = Eigen::AngleAxisd(delta_azimut_rad, Vec3d::UnitZ()); m_view_rotation *= rot_z * Eigen::AngleAxisd(delta_zenit_rad, rot_z.inverse() * get_dir_right()); m_view_rotation.normalize(); m_view_matrix.fromPositionOrientationScale(m_view_rotation * (- m_target) + translation, m_view_rotation, Vec3d(1., 1., 1.)); @@ -299,10 +281,10 @@ void Camera::rotate_on_sphere(double delta_azimut_rad, double delta_zenit_rad, b // Virtual trackball, rotate around an axis, where the eucledian norm of the axis gives the rotation angle in radians. void Camera::rotate_local_around_target(const Vec3d& rotation_rad) { - double angle = rotation_rad.norm(); + const double angle = rotation_rad.norm(); if (std::abs(angle) > EPSILON) { - Vec3d translation = m_view_matrix.translation() + m_view_rotation * m_target; - Vec3d axis = m_view_rotation.conjugate() * rotation_rad.normalized(); + const Vec3d translation = m_view_matrix.translation() + m_view_rotation * m_target; + const Vec3d axis = m_view_rotation.conjugate() * rotation_rad.normalized(); m_view_rotation *= Eigen::Quaterniond(Eigen::AngleAxisd(angle, axis)); m_view_rotation.normalize(); m_view_matrix.fromPositionOrientationScale(m_view_rotation * (-m_target) + translation, m_view_rotation, Vec3d(1., 1., 1.)); @@ -310,18 +292,13 @@ void Camera::rotate_local_around_target(const Vec3d& rotation_rad) } } -double Camera::min_zoom() const -{ - return 0.7 * calc_zoom_to_bounding_box_factor(m_scene_box); -} - std::pair Camera::calc_tight_frustrum_zs_around(const BoundingBoxf3& box) const { std::pair ret; auto& [near_z, far_z] = ret; // box in eye space - BoundingBoxf3 eye_box = box.transformed(m_view_matrix); + const BoundingBoxf3 eye_box = box.transformed(m_view_matrix); near_z = -eye_box.max(2); far_z = -eye_box.min(2); @@ -331,14 +308,14 @@ std::pair Camera::calc_tight_frustrum_zs_around(const BoundingBo // ensure min size if (far_z - near_z < FrustrumMinZRange) { - double mid_z = 0.5 * (near_z + far_z); - double half_size = 0.5 * FrustrumMinZRange; + const double mid_z = 0.5 * (near_z + far_z); + const double half_size = 0.5 * FrustrumMinZRange; near_z = mid_z - half_size; far_z = mid_z + half_size; } if (near_z < FrustrumMinNearZ) { - double delta = FrustrumMinNearZ - near_z; + const double delta = FrustrumMinNearZ - near_z; set_distance(m_distance + delta); near_z += delta; far_z += delta; @@ -358,45 +335,43 @@ std::pair Camera::calc_tight_frustrum_zs_around(const BoundingBo double Camera::calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, double margin_factor) const { - double max_bb_size = box.max_size(); + const double max_bb_size = box.max_size(); if (max_bb_size == 0.0) return -1.0; // project the box vertices on a plane perpendicular to the camera forward axis // then calculates the vertices coordinate on this plane along the camera xy axes - Vec3d right = get_dir_right(); - Vec3d up = get_dir_up(); - Vec3d forward = get_dir_forward(); - - Vec3d bb_center = box.center(); + const Vec3d right = get_dir_right(); + const Vec3d up = get_dir_up(); + const Vec3d forward = get_dir_forward(); + const Vec3d bb_center = box.center(); // box vertices in world space - std::vector vertices; - vertices.reserve(8); - vertices.push_back(box.min); - vertices.emplace_back(box.max(0), box.min(1), box.min(2)); - vertices.emplace_back(box.max(0), box.max(1), box.min(2)); - vertices.emplace_back(box.min(0), box.max(1), box.min(2)); - vertices.emplace_back(box.min(0), box.min(1), box.max(2)); - vertices.emplace_back(box.max(0), box.min(1), box.max(2)); - vertices.push_back(box.max); - vertices.emplace_back(box.min(0), box.max(1), box.max(2)); + const std::vector vertices = { + box.min, + { box.max(0), box.min(1), box.min(2) }, + { box.max(0), box.max(1), box.min(2) }, + { box.min(0), box.max(1), box.min(2) }, + { box.min(0), box.min(1), box.max(2) }, + { box.max(0), box.min(1), box.max(2) }, + box.max, + { box.min(0), box.max(1), box.max(2) } + }; double min_x = DBL_MAX; double min_y = DBL_MAX; double max_x = -DBL_MAX; double max_y = -DBL_MAX; - for (const Vec3d& v : vertices) - { + for (const Vec3d& v : vertices) { // project vertex on the plane perpendicular to camera forward axis - Vec3d pos = v - bb_center; - Vec3d proj_on_plane = pos - pos.dot(forward) * forward; + const Vec3d pos = v - bb_center; + const Vec3d proj_on_plane = pos - pos.dot(forward) * forward; // calculates vertex coordinate along camera xy axes - double x_on_plane = proj_on_plane.dot(right); - double y_on_plane = proj_on_plane.dot(up); + const double x_on_plane = proj_on_plane.dot(right); + const double y_on_plane = proj_on_plane.dot(up); min_x = std::min(min_x, x_on_plane); min_y = std::min(min_y, y_on_plane); @@ -406,7 +381,7 @@ double Camera::calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, double double dx = max_x - min_x; double dy = max_y - min_y; - if ((dx <= 0.0) || (dy <= 0.0)) + if (dx <= 0.0 || dy <= 0.0) return -1.0f; dx *= margin_factor; @@ -423,13 +398,12 @@ double Camera::calc_zoom_to_volumes_factor(const GLVolumePtrs& volumes, Vec3d& c // project the volumes vertices on a plane perpendicular to the camera forward axis // then calculates the vertices coordinate on this plane along the camera xy axes - Vec3d right = get_dir_right(); - Vec3d up = get_dir_up(); - Vec3d forward = get_dir_forward(); + const Vec3d right = get_dir_right(); + const Vec3d up = get_dir_up(); + const Vec3d forward = get_dir_forward(); BoundingBoxf3 box; - for (const GLVolume* volume : volumes) - { + for (const GLVolume* volume : volumes) { box.merge(volume->transformed_bounding_box()); } center = box.center(); @@ -439,24 +413,22 @@ double Camera::calc_zoom_to_volumes_factor(const GLVolumePtrs& volumes, Vec3d& c double max_x = -DBL_MAX; double max_y = -DBL_MAX; - for (const GLVolume* volume : volumes) - { + for (const GLVolume* volume : volumes) { const Transform3d& transform = volume->world_matrix(); const TriangleMesh* hull = volume->convex_hull(); if (hull == nullptr) continue; - for (const Vec3f& vertex : hull->its.vertices) - { - Vec3d v = transform * vertex.cast(); + for (const Vec3f& vertex : hull->its.vertices) { + const Vec3d v = transform * vertex.cast(); // project vertex on the plane perpendicular to camera forward axis - Vec3d pos = v - center; - Vec3d proj_on_plane = pos - pos.dot(forward) * forward; + const Vec3d pos = v - center; + const Vec3d proj_on_plane = pos - pos.dot(forward) * forward; // calculates vertex coordinate along camera xy axes - double x_on_plane = proj_on_plane.dot(right); - double y_on_plane = proj_on_plane.dot(up); + const double x_on_plane = proj_on_plane.dot(right); + const double y_on_plane = proj_on_plane.dot(up); min_x = std::min(min_x, x_on_plane); min_y = std::min(min_y, y_on_plane); @@ -467,8 +439,8 @@ double Camera::calc_zoom_to_volumes_factor(const GLVolumePtrs& volumes, Vec3d& c center += 0.5 * (max_x + min_x) * right + 0.5 * (max_y + min_y) * up; - double dx = margin_factor * (max_x - min_x); - double dy = margin_factor * (max_y - min_y); + const double dx = margin_factor * (max_x - min_x); + const double dy = margin_factor * (max_y - min_y); if (dx <= 0.0 || dy <= 0.0) return -1.0f; @@ -486,13 +458,13 @@ void Camera::set_distance(double distance) const void Camera::look_at(const Vec3d& position, const Vec3d& target, const Vec3d& up) { - Vec3d unit_z = (position - target).normalized(); - Vec3d unit_x = up.cross(unit_z).normalized(); - Vec3d unit_y = unit_z.cross(unit_x).normalized(); + const Vec3d unit_z = (position - target).normalized(); + const Vec3d unit_x = up.cross(unit_z).normalized(); + const Vec3d unit_y = unit_z.cross(unit_x).normalized(); m_target = target; m_distance = (position - target).norm(); - Vec3d new_position = m_target + m_distance * unit_z; + const Vec3d new_position = m_target + m_distance * unit_z; m_view_matrix(0, 0) = unit_x(0); m_view_matrix(0, 1) = unit_x(1); @@ -524,10 +496,10 @@ void Camera::look_at(const Vec3d& position, const Vec3d& target, const Vec3d& up void Camera::set_default_orientation() { m_zenit = 45.0f; - double theta_rad = Geometry::deg2rad(-(double)m_zenit); - double phi_rad = Geometry::deg2rad(45.0); - double sin_theta = ::sin(theta_rad); - Vec3d camera_pos = m_target + m_distance * Vec3d(sin_theta * ::sin(phi_rad), sin_theta * ::cos(phi_rad), ::cos(theta_rad)); + const double theta_rad = Geometry::deg2rad(-(double)m_zenit); + const double phi_rad = Geometry::deg2rad(45.0); + const double sin_theta = ::sin(theta_rad); + const Vec3d camera_pos = m_target + m_distance * Vec3d(sin_theta * ::sin(phi_rad), sin_theta * ::cos(phi_rad), ::cos(theta_rad)); m_view_rotation = Eigen::AngleAxisd(theta_rad, Vec3d::UnitX()) * Eigen::AngleAxisd(phi_rad, Vec3d::UnitZ()); m_view_rotation.normalize(); m_view_matrix.fromPositionOrientationScale(m_view_rotation * (- camera_pos), m_view_rotation, Vec3d(1., 1., 1.)); @@ -542,9 +514,9 @@ Vec3d Camera::validate_target(const Vec3d& target) const test_box.scale(ScaleFactor); test_box.translate(m_scene_box.center()); - return Vec3d(std::clamp(target(0), test_box.min(0), test_box.max(0)), - std::clamp(target(1), test_box.min(1), test_box.max(1)), - std::clamp(target(2), test_box.min(2), test_box.max(2))); + return { std::clamp(target(0), test_box.min(0), test_box.max(0)), + std::clamp(target(1), test_box.min(1), test_box.max(1)), + std::clamp(target(2), test_box.min(2), test_box.max(2)) }; } void Camera::update_zenit() diff --git a/src/slic3r/GUI/Camera.hpp b/src/slic3r/GUI/Camera.hpp index 574a6eed76..1abb430ca9 100644 --- a/src/slic3r/GUI/Camera.hpp +++ b/src/slic3r/GUI/Camera.hpp @@ -26,7 +26,7 @@ struct Camera Num_types }; - bool requires_zoom_to_bed; + bool requires_zoom_to_bed{ false }; private: EType m_type{ Perspective }; @@ -48,13 +48,13 @@ private: BoundingBoxf3 m_scene_box; public: - Camera(); + Camera() { set_default_orientation(); } EType get_type() const { return m_type; } std::string get_type_as_string() const; void set_type(EType type); // valid values for type: "0" -> ortho, "1" -> perspective - void set_type(const std::string& type); + void set_type(const std::string& type) { set_type((type == "1") ? Perspective : Ortho); } void select_next_type(); void enable_update_config_on_type_change(bool enable) { m_update_config_on_type_change_enabled = enable; } @@ -67,7 +67,7 @@ public: double get_zoom() const { return m_zoom; } double get_inv_zoom() const { assert(m_zoom != 0.0); return 1.0 / m_zoom; } - void update_zoom(double delta_zoom); + void update_zoom(double delta_zoom) { set_zoom(m_zoom / (1.0 - std::max(std::min(delta_zoom, 4.0), -4.0) * 0.1)); } void set_zoom(double zoom); const BoundingBoxf3& get_scene_box() const { return m_scene_box; } @@ -127,7 +127,7 @@ public: void look_at(const Vec3d& position, const Vec3d& target, const Vec3d& up); double max_zoom() const { return 250.0; } - double min_zoom() const; + double min_zoom() const { return 0.7 * calc_zoom_to_bounding_box_factor(m_scene_box); } private: // returns tight values for nearZ and farZ plane around the given bounding box From 4c464b35f97441985b0e3ee6f12d5ee62ad07261 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 16 Apr 2021 15:25:03 +0200 Subject: [PATCH 261/285] Removed mutable members from class Selection --- src/slic3r/GUI/Selection.cpp | 88 ++++++++++++++++-------------------- src/slic3r/GUI/Selection.hpp | 14 +++--- 2 files changed, 45 insertions(+), 57 deletions(-) diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index faf25ff8bc..2acb8cb85b 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1102,39 +1102,32 @@ void Selection::erase() if (is_single_full_object()) wxGetApp().obj_list()->delete_from_model_and_list(ItemType::itObject, get_object_idx(), 0); - else if (is_multiple_full_object()) - { + else if (is_multiple_full_object()) { std::vector items; items.reserve(m_cache.content.size()); - for (ObjectIdxsToInstanceIdxsMap::iterator it = m_cache.content.begin(); it != m_cache.content.end(); ++it) - { + for (ObjectIdxsToInstanceIdxsMap::iterator it = m_cache.content.begin(); it != m_cache.content.end(); ++it) { items.emplace_back(ItemType::itObject, it->first, 0); } wxGetApp().obj_list()->delete_from_model_and_list(items); } - else if (is_multiple_full_instance()) - { + else if (is_multiple_full_instance()) { std::set> instances_idxs; - for (ObjectIdxsToInstanceIdxsMap::iterator obj_it = m_cache.content.begin(); obj_it != m_cache.content.end(); ++obj_it) - { - for (InstanceIdxsList::reverse_iterator inst_it = obj_it->second.rbegin(); inst_it != obj_it->second.rend(); ++inst_it) - { + for (ObjectIdxsToInstanceIdxsMap::iterator obj_it = m_cache.content.begin(); obj_it != m_cache.content.end(); ++obj_it) { + for (InstanceIdxsList::reverse_iterator inst_it = obj_it->second.rbegin(); inst_it != obj_it->second.rend(); ++inst_it) { instances_idxs.insert(std::make_pair(obj_it->first, *inst_it)); } } std::vector items; items.reserve(instances_idxs.size()); - for (const std::pair& i : instances_idxs) - { + for (const std::pair& i : instances_idxs) { items.emplace_back(ItemType::itInstance, i.first, i.second); } wxGetApp().obj_list()->delete_from_model_and_list(items); } else if (is_single_full_instance()) wxGetApp().obj_list()->delete_from_model_and_list(ItemType::itInstance, get_object_idx(), get_instance_idx()); - else if (is_mixed()) - { + else if (is_mixed()) { std::set items_set; std::map volumes_in_obj; @@ -1186,11 +1179,9 @@ void Selection::erase() wxGetApp().obj_list()->delete_from_model_and_list(items); } - else - { + else { std::set> volumes_idxs; - for (unsigned int i : m_list) - { + for (unsigned int i : m_list) { const GLVolume* v = (*m_volumes)[i]; // Only remove volumes associated with ModelVolumes from the object list. // Temporary meshes (SLA supports or pads) are not managed by the object list. @@ -1200,8 +1191,7 @@ void Selection::erase() std::vector items; items.reserve(volumes_idxs.size()); - for (const std::pair& v : volumes_idxs) - { + for (const std::pair& v : volumes_idxs) { items.emplace_back(ItemType::itVolume, v.first, v.second); } @@ -1214,7 +1204,7 @@ void Selection::render(float scale_factor) const if (!m_valid || is_empty()) return; - m_scale_factor = scale_factor; + *const_cast(&m_scale_factor) = scale_factor; // render cumulative bounding box of selected volumes render_selected_volumes(); @@ -1224,7 +1214,7 @@ void Selection::render(float scale_factor) const #if ENABLE_RENDER_SELECTION_CENTER void Selection::render_center(bool gizmo_is_dragging) const { - if (!m_valid || is_empty() || (m_quadric == nullptr)) + if (!m_valid || is_empty() || m_quadric == nullptr) return; Vec3d center = gizmo_is_dragging ? m_cache.dragging_center : get_bounding_box().center(); @@ -1250,8 +1240,7 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field) const GLShaderProgram* shader = nullptr; - if (!boost::starts_with(sidebar_field, "layer")) - { + if (!boost::starts_with(sidebar_field, "layer")) { shader = wxGetApp().get_shader("gouraud_light"); if (shader == nullptr) return; @@ -1735,18 +1724,16 @@ void Selection::do_remove_volume(unsigned int volume_idx) void Selection::do_remove_instance(unsigned int object_idx, unsigned int instance_idx) { - for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) - { + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) { GLVolume* v = (*m_volumes)[i]; - if ((v->object_idx() == (int)object_idx) && (v->instance_idx() == (int)instance_idx)) + if (v->object_idx() == (int)object_idx && v->instance_idx() == (int)instance_idx) do_remove_volume(i); } } void Selection::do_remove_object(unsigned int object_idx) { - for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) - { + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) { GLVolume* v = (*m_volumes)[i]; if (v->object_idx() == (int)object_idx) do_remove_volume(i); @@ -1755,47 +1742,48 @@ void Selection::do_remove_object(unsigned int object_idx) void Selection::calc_bounding_box() const { - m_bounding_box = BoundingBoxf3(); - if (m_valid) - { - for (unsigned int i : m_list) - { - m_bounding_box.merge((*m_volumes)[i]->transformed_convex_hull_bounding_box()); + BoundingBoxf3* bounding_box = const_cast(&m_bounding_box); + *bounding_box = BoundingBoxf3(); + if (m_valid) { + for (unsigned int i : m_list) { + bounding_box->merge((*m_volumes)[i]->transformed_convex_hull_bounding_box()); } } - m_bounding_box_dirty = false; + *const_cast(&m_bounding_box_dirty) = false; } void Selection::calc_unscaled_instance_bounding_box() const { - m_unscaled_instance_bounding_box = BoundingBoxf3(); - if (m_valid) { - for (unsigned int i : m_list) { - const GLVolume &volume = *(*m_volumes)[i]; + BoundingBoxf3* unscaled_instance_bounding_box = const_cast(&m_unscaled_instance_bounding_box); + *unscaled_instance_bounding_box = BoundingBoxf3(); + if (m_valid) { + for (unsigned int i : m_list) { + const GLVolume& volume = *(*m_volumes)[i]; if (volume.is_modifier) continue; - Transform3d trafo = volume.get_instance_transformation().get_matrix(false, false, true, false) * volume.get_volume_transformation().get_matrix(); - trafo.translation()(2) += volume.get_sla_shift_z(); - m_unscaled_instance_bounding_box.merge(volume.transformed_convex_hull_bounding_box(trafo)); - } - } - m_unscaled_instance_bounding_box_dirty = false; + Transform3d trafo = volume.get_instance_transformation().get_matrix(false, false, true, false) * volume.get_volume_transformation().get_matrix(); + trafo.translation()(2) += volume.get_sla_shift_z(); + unscaled_instance_bounding_box->merge(volume.transformed_convex_hull_bounding_box(trafo)); + } + } + *const_cast(&m_unscaled_instance_bounding_box_dirty) = false; } void Selection::calc_scaled_instance_bounding_box() const { - m_scaled_instance_bounding_box = BoundingBoxf3(); + BoundingBoxf3* scaled_instance_bounding_box = const_cast(&m_scaled_instance_bounding_box); + *scaled_instance_bounding_box = BoundingBoxf3(); if (m_valid) { for (unsigned int i : m_list) { - const GLVolume &volume = *(*m_volumes)[i]; + const GLVolume& volume = *(*m_volumes)[i]; if (volume.is_modifier) continue; Transform3d trafo = volume.get_instance_transformation().get_matrix(false, false, false, false) * volume.get_volume_transformation().get_matrix(); trafo.translation()(2) += volume.get_sla_shift_z(); - m_scaled_instance_bounding_box.merge(volume.transformed_convex_hull_bounding_box(trafo)); + scaled_instance_bounding_box->merge(volume.transformed_convex_hull_bounding_box(trafo)); } } - m_scaled_instance_bounding_box_dirty = false; + *const_cast(&m_scaled_instance_bounding_box_dirty) = false; } void Selection::render_selected_volumes() const diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index a9095adcbf..8bb418baac 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -206,14 +206,14 @@ private: IndicesList m_list; Cache m_cache; Clipboard m_clipboard; - mutable BoundingBoxf3 m_bounding_box; - mutable bool m_bounding_box_dirty; + BoundingBoxf3 m_bounding_box; + bool m_bounding_box_dirty; // Bounding box of a selection, with no instance scaling applied. This bounding box // is useful for absolute scaling of tilted objects in world coordinate space. - mutable BoundingBoxf3 m_unscaled_instance_bounding_box; - mutable bool m_unscaled_instance_bounding_box_dirty; - mutable BoundingBoxf3 m_scaled_instance_bounding_box; - mutable bool m_scaled_instance_bounding_box_dirty; + BoundingBoxf3 m_unscaled_instance_bounding_box; + bool m_unscaled_instance_bounding_box_dirty; + BoundingBoxf3 m_scaled_instance_bounding_box; + bool m_scaled_instance_bounding_box_dirty; #if ENABLE_RENDER_SELECTION_CENTER GLUquadricObj* m_quadric; @@ -222,7 +222,7 @@ private: GLModel m_arrow; GLModel m_curved_arrow; - mutable float m_scale_factor; + float m_scale_factor; public: Selection(); From 4da8de5f4909a77ca3d07e3b6540f50508f969c3 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 16 Apr 2021 15:49:37 +0200 Subject: [PATCH 262/285] Removed mutable members from class GLToolbar --- src/slic3r/GUI/GLToolbar.cpp | 90 ++++++++++++++---------------------- src/slic3r/GUI/GLToolbar.hpp | 6 +-- 2 files changed, 38 insertions(+), 58 deletions(-) diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index 79e7ea1c64..0e6a4ce291 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -428,8 +428,7 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent) bool processed = false; // mouse anywhere - if (!evt.Dragging() && !evt.Leaving() && !evt.Entering() && (m_mouse_capture.parent != nullptr)) - { + if (!evt.Dragging() && !evt.Leaving() && !evt.Entering() && m_mouse_capture.parent != nullptr) { if (m_mouse_capture.any() && (evt.LeftUp() || evt.MiddleUp() || evt.RightUp())) { // prevents loosing selection into the scene if mouse down was done inside the toolbar and mouse up was down outside it, // as when switching between views @@ -441,38 +440,31 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent) if (evt.Moving()) update_hover_state(mouse_pos, parent); - else if (evt.LeftUp()) - { - if (m_mouse_capture.left) - { + else if (evt.LeftUp()) { + if (m_mouse_capture.left) { processed = true; m_mouse_capture.left = false; } else return false; } - else if (evt.MiddleUp()) - { - if (m_mouse_capture.middle) - { + else if (evt.MiddleUp()) { + if (m_mouse_capture.middle) { processed = true; m_mouse_capture.middle = false; } else return false; } - else if (evt.RightUp()) - { - if (m_mouse_capture.right) - { + else if (evt.RightUp()) { + if (m_mouse_capture.right) { processed = true; m_mouse_capture.right = false; } else return false; } - else if (evt.Dragging()) - { + else if (evt.Dragging()) { if (m_mouse_capture.any()) // if the button down was done on this toolbar, prevent from dragging into the scene processed = true; @@ -481,35 +473,29 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent) } int item_id = contains_mouse(mouse_pos, parent); - if (item_id != -1) - { + if (item_id != -1) { // mouse inside toolbar - if (evt.LeftDown() || evt.LeftDClick()) - { + if (evt.LeftDown() || evt.LeftDClick()) { m_mouse_capture.left = true; m_mouse_capture.parent = &parent; processed = true; - if ((item_id != -2) && !m_items[item_id]->is_separator() && !m_items[item_id]->is_disabled() && - ((m_pressed_toggable_id == -1) || (m_items[item_id]->get_last_action_type() == GLToolbarItem::Left))) - { + if (item_id != -2 && !m_items[item_id]->is_separator() && !m_items[item_id]->is_disabled() && + (m_pressed_toggable_id == -1 || m_items[item_id]->get_last_action_type() == GLToolbarItem::Left)) { // mouse is inside an icon do_action(GLToolbarItem::Left, item_id, parent, true); parent.set_as_dirty(); } } - else if (evt.MiddleDown()) - { + else if (evt.MiddleDown()) { m_mouse_capture.middle = true; m_mouse_capture.parent = &parent; } - else if (evt.RightDown()) - { + else if (evt.RightDown()) { m_mouse_capture.right = true; m_mouse_capture.parent = &parent; processed = true; - if ((item_id != -2) && !m_items[item_id]->is_separator() && !m_items[item_id]->is_disabled() && - ((m_pressed_toggable_id == -1) || (m_items[item_id]->get_last_action_type() == GLToolbarItem::Right))) - { + if (item_id != -2 && !m_items[item_id]->is_separator() && !m_items[item_id]->is_disabled() && + (m_pressed_toggable_id == -1 || m_items[item_id]->get_last_action_type() == GLToolbarItem::Right)) { // mouse is inside an icon do_action(GLToolbarItem::Right, item_id, parent, true); parent.set_as_dirty(); @@ -522,24 +508,26 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent) void GLToolbar::calc_layout() const { - switch (m_layout.type) + Layout* layout = const_cast(&m_layout); + + switch (layout->type) { default: case Layout::Horizontal: { - m_layout.width = get_width_horizontal(); - m_layout.height = get_height_horizontal(); + layout->width = get_width_horizontal(); + layout->height = get_height_horizontal(); break; } case Layout::Vertical: { - m_layout.width = get_width_vertical(); - m_layout.height = get_height_vertical(); + layout->width = get_width_vertical(); + layout->height = get_height_vertical(); break; } } - m_layout.dirty = false; + layout->dirty = false; } float GLToolbar::get_width_horizontal() const @@ -1196,19 +1184,17 @@ void GLToolbar::render_vertical(const GLCanvas3D& parent) const left += scaled_border; top -= scaled_border; - if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0)) + if (tex_id == 0 || tex_width <= 0 || tex_height <= 0) return; // renders icons - for (const GLToolbarItem* item : m_items) - { + for (const GLToolbarItem* item : m_items) { if (!item->is_visible()) continue; if (item->is_separator()) top -= separator_stride; - else - { + else { item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(m_layout.icons_size * m_layout.scale)); top -= icon_stride; } @@ -1219,16 +1205,14 @@ bool GLToolbar::generate_icons_texture() const { std::string path = resources_dir() + "/icons/"; std::vector filenames; - for (GLToolbarItem* item : m_items) - { + for (GLToolbarItem* item : m_items) { const std::string& icon_filename = item->get_icon_filename(); if (!icon_filename.empty()) filenames.push_back(path + icon_filename); } std::vector> states; - if (m_type == Normal) - { + if (m_type == Normal) { states.push_back({ 1, false }); // Normal states.push_back({ 0, false }); // Pressed states.push_back({ 2, false }); // Disabled @@ -1236,8 +1220,7 @@ bool GLToolbar::generate_icons_texture() const states.push_back({ 0, false }); // HoverPressed states.push_back({ 2, false }); // HoverDisabled } - else - { + else { states.push_back({ 1, false }); // Normal states.push_back({ 1, true }); // Pressed states.push_back({ 1, false }); // Disabled @@ -1251,9 +1234,9 @@ bool GLToolbar::generate_icons_texture() const // if (sprite_size_px % 2 != 0) // sprite_size_px += 1; - bool res = m_icons_texture.load_from_svg_files_as_sprites_array(filenames, states, sprite_size_px, false); + bool res = const_cast(&m_icons_texture)->load_from_svg_files_as_sprites_array(filenames, states, sprite_size_px, false); if (res) - m_icons_texture_dirty = false; + *const_cast(&m_icons_texture_dirty) = false; return res; } @@ -1262,8 +1245,7 @@ bool GLToolbar::update_items_visibility() { bool ret = false; - for (GLToolbarItem* item : m_items) - { + for (GLToolbarItem* item : m_items) { ret |= item->update_visibility(); } @@ -1272,12 +1254,10 @@ bool GLToolbar::update_items_visibility() // updates separators visibility to avoid having two of them consecutive bool any_item_visible = false; - for (GLToolbarItem* item : m_items) - { + for (GLToolbarItem* item : m_items) { if (!item->is_separator()) any_item_visible |= item->is_visible(); - else - { + else { item->set_visible(any_item_visible); any_item_visible = false; } diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index 74e18de975..3237c44953 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -233,10 +233,10 @@ private: EType m_type; std::string m_name; bool m_enabled; - mutable GLTexture m_icons_texture; - mutable bool m_icons_texture_dirty; + GLTexture m_icons_texture; + bool m_icons_texture_dirty; BackgroundTexture m_background_texture; - mutable Layout m_layout; + Layout m_layout; ItemsList m_items; From 2ffcf97be155c3f2060c19958e40f08b86710a78 Mon Sep 17 00:00:00 2001 From: Pascal de Bruijn Date: Fri, 16 Apr 2021 19:30:33 +0200 Subject: [PATCH 263/285] creality.ini: more accurate spool weights for Devil Design I just noticed Devil Design uses identical spools to Extrudr NX-2 --- resources/profiles/Creality.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index 80c76932d0..9771e7ac6c 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -637,7 +637,7 @@ first_layer_bed_temperature = 60 filament_cost = 19.00 filament_density = 1.24 filament_colour = #FF0000 -filament_spool_weight = 250 +filament_spool_weight = 256 [filament:Devil Design PLA (Galaxy) @CREALITY] inherits = *PLA* @@ -649,7 +649,7 @@ first_layer_bed_temperature = 65 filament_cost = 19.00 filament_density = 1.24 filament_colour = #FF0000 -filament_spool_weight = 250 +filament_spool_weight = 256 [filament:Extrudr PLA NX2 @CREALITY] inherits = *PLA* From d33cffdd1e196bc29354b9eeb7a2f4289522c106 Mon Sep 17 00:00:00 2001 From: Pascal de Bruijn Date: Fri, 16 Apr 2021 19:32:32 +0200 Subject: [PATCH 264/285] creality.ini: remove parentheses for galaxy pla filament since we've not using those for other filament subtypes --- resources/profiles/Creality.ini | 61 +++++++++++++++++---------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index 9771e7ac6c..95ce8a0dce 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -21,7 +21,7 @@ technology = FFF family = ENDER bed_model = ender3_bed.stl bed_texture = ender3.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:ENDER3BLTOUCH] name = Creality Ender-3 BLTouch @@ -30,7 +30,7 @@ technology = FFF family = ENDER bed_model = ender3_bed.stl bed_texture = ender3.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:ENDER3V2] name = Creality Ender-3 V2 @@ -39,7 +39,7 @@ technology = FFF family = ENDER bed_model = ender3v2_bed.stl bed_texture = ender3v2.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:ENDER3MAX] name = Creality Ender-3 Max @@ -48,7 +48,7 @@ technology = FFF family = ENDER bed_model = cr10v2_bed.stl bed_texture = cr10spro.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:ENDER4] name = Creality Ender-4 @@ -57,7 +57,7 @@ technology = FFF family = ENDER bed_model = ender3v2_bed.stl bed_texture = ender3v2.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:ENDER5] name = Creality Ender-5 @@ -66,7 +66,7 @@ technology = FFF family = ENDER bed_model = ender3_bed.stl bed_texture = ender3.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:ENDER5PLUS] name = Creality Ender-5 Plus @@ -75,7 +75,7 @@ technology = FFF family = ENDER bed_model = ender5plus_bed.stl bed_texture = ender5plus.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:ENDER6] name = Creality Ender-6 @@ -84,7 +84,7 @@ technology = FFF family = ENDER bed_model = ender6_bed.stl bed_texture = ender6.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:ENDER2] name = Creality Ender-2 @@ -93,7 +93,7 @@ technology = FFF family = ENDER bed_model = ender2_bed.stl bed_texture = ender2.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR5PRO] name = Creality CR-5 Pro @@ -102,7 +102,7 @@ technology = FFF family = CR bed_model = cr5pro_bed.stl bed_texture = cr5pro.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR5PROH] name = Creality CR-5 Pro H @@ -111,7 +111,7 @@ technology = FFF family = CR bed_model = cr5pro_bed.stl bed_texture = cr5pro.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR6SE] name = Creality CR-6 SE @@ -120,7 +120,7 @@ technology = FFF family = CR bed_model = cr6se_bed.stl bed_texture = cr6se.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR6MAX] name = Creality CR-6 Max @@ -129,7 +129,7 @@ technology = FFF family = CR bed_model = cr10s4_bed.stl bed_texture = cr10s4.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR10MINI] name = Creality CR-10 Mini @@ -138,7 +138,7 @@ technology = FFF family = CR bed_model = cr10mini_bed.stl bed_texture = cr10mini.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR10MAX] name = Creality CR-10 Max @@ -147,7 +147,7 @@ technology = FFF family = CR bed_model = cr10max_bed.stl bed_texture = cr10max.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR10] name = Creality CR-10 @@ -156,7 +156,7 @@ technology = FFF family = CR bed_model = cr10_bed.stl bed_texture = cr10.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR10V2] name = Creality CR-10 V2 @@ -165,7 +165,7 @@ technology = FFF family = CR bed_model = cr10v2_bed.stl bed_texture = cr10.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR10V3] name = Creality CR-10 V3 @@ -174,7 +174,7 @@ technology = FFF family = CR bed_model = cr10v2_bed.stl bed_texture = cr10.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR10S] name = Creality CR-10 S @@ -183,7 +183,7 @@ technology = FFF family = CR bed_model = cr10_bed.stl bed_texture = cr10.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR10SPRO] name = Creality CR-10 S Pro @@ -192,7 +192,7 @@ technology = FFF family = CR bed_model = cr10v2_bed.stl bed_texture = cr10spro.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR10SPROV2] name = Creality CR-10 S Pro V2 @@ -201,7 +201,7 @@ technology = FFF family = CR bed_model = cr10v2_bed.stl bed_texture = cr10.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR10S4] name = Creality CR-10 S4 @@ -210,7 +210,7 @@ technology = FFF family = CR bed_model = cr10s4_bed.stl bed_texture = cr10s4.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR10S5] name = Creality CR-10 S5 @@ -219,7 +219,7 @@ technology = FFF family = CR bed_model = cr10s5_bed.stl bed_texture = cr10s5.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR20] name = Creality CR-20 @@ -228,7 +228,7 @@ technology = FFF family = CR bed_model = ender3_bed.stl bed_texture = cr20.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR20PRO] name = Creality CR-20 Pro @@ -237,7 +237,7 @@ technology = FFF family = CR bed_model = ender3_bed.stl bed_texture = cr20.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR200B] name = Creality CR-200B @@ -246,7 +246,7 @@ technology = FFF family = CR bed_model = cr200b_bed.stl bed_texture = cr200b.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY [printer_model:CR8] name = Creality CR-8 @@ -255,7 +255,7 @@ technology = FFF family = CR bed_model = cr8_bed.stl bed_texture = cr8.svg -default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY #[printer_model:CRX] #name = Creality CR-X @@ -264,7 +264,7 @@ default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @ #family = CR-X #bed_model = cr10v2_bed.stl #bed_texture = cr10spro.svg -#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY #[printer_model:CRXPRO] #name = Creality CR-X Pro @@ -273,7 +273,7 @@ default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @ #family = CR-X #bed_model = cr10v2_bed.stl #bed_texture = cr10spro.svg -#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA (Galaxy) @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY +#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY # All presets starting with asterisk, for example *common*, are intermediate and they will # not make it into the user interface. @@ -639,8 +639,9 @@ filament_density = 1.24 filament_colour = #FF0000 filament_spool_weight = 256 -[filament:Devil Design PLA (Galaxy) @CREALITY] +[filament:Devil Design PLA Galaxy @CREALITY] inherits = *PLA* +renamed_from = "Devil Design PLA (Galaxy) @CREALITY" filament_vendor = Devil Design temperature = 225 bed_temperature = 65 From 321a2b7639d70217bd12c8efc242409f0dec4991 Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Mon, 19 Apr 2021 15:48:07 +0200 Subject: [PATCH 265/285] Disabled thick bridges, updated support settings. Bundle refactoring. --- resources/profiles/PrusaResearch.idx | 320 ++-- resources/profiles/PrusaResearch.ini | 2072 ++++++++++---------------- 2 files changed, 964 insertions(+), 1428 deletions(-) diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx index 8c14026ce1..dc3c79cd55 100644 --- a/resources/profiles/PrusaResearch.idx +++ b/resources/profiles/PrusaResearch.idx @@ -1,159 +1,161 @@ -min_slic3r_version = 2.3.0-rc1 -1.2.4 Updated cost/density values in filament settings. Various changes in print settings. -1.2.3 Updated firmware version. Updated end g-code in MMU2 printer profiles. -1.2.2 Added Prusament PVB filament profile. Added 0.8mm nozzle profiles. -1.2.1 Updated FW version for MK2.5 family printers. -1.2.0 Added full_fan_speed_layer value for PETG. Increased support interface spacing for 0.6mm nozzle profiles. Updated firmware version. -min_slic3r_version = 2.3.0-beta2 -1.2.0-beta1 Updated end g-code. Added full_fan_speed_layer values. -min_slic3r_version = 2.3.0-beta0 -1.2.0-beta0 Adjusted infill anchor limits. Added filament spool weights. -min_slic3r_version = 2.3.0-alpha4 -1.2.0-alpha1 Renamed MK3S and MINI printer profiles. Updated end g-code (MINI). Added new SLA materials and filament profiles. -1.2.0-alpha0 Added filament spool weights -min_slic3r_version = 2.2.0-alpha3 -1.1.13 Updated firmware version. Updated end g-code in MMU2 printer profiles. -1.1.12 Added Prusament PVB filament profile. Added 0.8mm nozzle profiles. -1.1.11 Renamed MK3S and MINI printer profiles. Updated end g-code (MINI). Added new SLA materials and filament profiles. -1.1.10 Updated firmware version. -1.1.9 Updated K values in filament profiles (linear advance). Added new filament profiles and SLA materials. -1.1.8 Updated start/end g-code scripts for MK3 family printer profiles (reduced extruder motor current for some print profiles). Added new filament and SLA material profiles. -1.1.7 Updated end g-code for MMU2 Single printer profiles. Added/updated filament and SLA material profiles. -1.1.6 Updated firmware version for MK2.5/S and MK3/S. -1.1.5 Updated MMU1 specific retraction settings for Prusament PC Blend -1.1.4 Added Prusament PC Blend filament profile. -1.1.3 Added SLA material and filament profile -1.1.2 Added renamed_from fields for PETG filaments to indicate that they were renamed from PET. -1.1.1 Added Verbatim and Fiberlogy PETG filament profiles. Updated auto cooling settings for ABS. -1.1.1-beta Updated for PrusaSlicer 2.2.0-beta -1.1.1-alpha4 Extended list of default filaments to be installed, top/bottom_solid_min_thickness defined, infill_acceleration changed etc -1.1.1-alpha3 Print bed textures are now configurable from the Preset Bundle. Requires PrusaSlicer 2.2.0-alpha3 and newer. -# The following line (max_slic3r_version) forces the users of PrusaSlicer 2.2.0-alpha3 and newer to update the profiles to 1.1.1-alpha3 and newer, -# so they will see the print bed. -max_slic3r_version = 2.2.0-alpha2 -min_slic3r_version = 2.2.0-alpha0 -1.1.1-alpha2 Bumped up config version, so our in house customer will get updated profiles. -1.1.0 Filament aliases, Creality profiles and other goodies for PrusaSlicer 2.2.0-alpha0 -min_slic3r_version = 2.1.1-beta0 -1.0.11 Updated firmware version. -1.0.10 Updated firmware version for MK2.5/S and MK3/S. -1.0.9 Updated firmware version for MK2.5/S and MK3/S. -1.0.8 Various changes in FFF profiles, new filaments/materials added. See changelog. -1.0.7 Updated layer height limits for MINI -1.0.6 Added Prusa MINI profiles -min_slic3r_version = 2.1.0-alpha0 -1.0.5 Added SLA materials -1.0.4 Updated firmware version and 0.25mm nozzle profiles -1.0.3 Added filament profiles -1.0.2 Added SLA materials -1.0.1 Updated MK3 firmware version check to 3.8.0, new soluble support profiles for 0.6mm nozzle diameter MMU2S printers. -1.0.0 Updated end G-code for the MMU2 profiles to lift the extruder at the end of print. Wipe tower bridging distance was made smaller for soluble supports. -1.0.0-beta1 Updated color for the ASA filaments to differ from the other filaments. Single extruder printers now have no extruder color assigned, obects and toolpaths will be colored with the color of the active filament. -1.0.0-beta0 Printer model checks in start G-codes, ASA filament profiles, limits on min / max SL1 exposition times -1.0.0-alpha2 Printer model and nozzle diameter check -1.0.0-alpha1 Added Prusament ASA profile -1.0.0-alpha0 Filament specific retract for PET and similar copolymers, and for FLEX -min_slic3r_version = 1.42.0-alpha6 -0.8.10 Updated firmware version. -0.8.9 Updated firmware version for MK2.5/S and MK3/S. -0.8.8 Updated firmware version for MK2.5/S and MK3/S. -0.8.7 Updated firmware version -0.8.6 Updated firmware version for MK2.5/S and MK3/S -0.8.5 Updated SL1 printer and material settings -0.8.4 Added Prusament ASA profile -0.8.3 FW version and SL1 materials update -0.8.2 FFF and SL1 settings update -0.8.1 Output settings and SLA materials update -0.8.0 Updated for the PrusaSlicer 2.0.0 final release -0.8.0-rc2 Updated firmware versions for MK2.5/S and MK3/S -0.8.0-rc1 Updated SLA profiles -0.8.0-rc Updated for the PrusaSlicer 2.0.0-rc release -0.8.0-beta4 Updated SLA profiles -0.8.0-beta3 Updated SLA profiles -0.8.0-beta2 Updated SLA profiles -0.8.0-beta1 Updated SLA profiles -0.8.0-beta Updated SLA profiles -0.8.0-alpha9 Updated SLA and FFF profiles -0.8.0-alpha8 Updated SLA profiles -0.8.0-alpha7 Updated SLA profiles -0.8.0-alpha6 Updated SLA profiles -min_slic3r_version = 1.42.0-alpha -0.8.0-alpha Updated SLA profiles -0.4.0-alpha4 Updated SLA profiles -0.4.0-alpha3 Update of SLA profiles -0.4.0-alpha2 First SLA profiles -min_slic3r_version = 1.41.3-alpha -0.4.12 Updated firmware version for MK2.5/S and MK3/S. -0.4.11 Updated firmware version for MK2.5/S and MK3/S. -0.4.10 Updated firmware version -0.4.9 Updated firmware version for MK2.5/S and MK3/S -0.4.8 MK2.5/3/S FW update -0.4.7 MK2/S/MMU FW update -0.4.6 Updated firmware versions for MK2.5/S and MK3/S -0.4.5 Enabled remaining time support for MK2/S/MMU1 -0.4.4 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt -0.4.3 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt -0.4.2 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt -0.4.1 New MK2.5S and MK3S FW versions -0.4.0 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt -min_slic3r_version = 1.41.1 -0.3.11 Updated firmware version for MK2.5/S and MK3/S. -0.3.10 Updated firmware version -0.3.9 Updated firmware version for MK2.5/S and MK3/S -0.3.8 MK2.5/3/S FW update -0.3.7 MK2/S/MMU FW update -0.3.6 Updated firmware versions for MK2.5 and MK3 -0.3.5 New MK2.5 and MK3 FW versions -0.3.4 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt -0.3.3 Prusament PETG released -0.3.2 New MK2.5 and MK3 FW versions -0.3.1 New MK2.5 and MK3 FW versions -0.3.0 New MK2.5 and MK3 FW version -min_slic3r_version = 1.41.0-alpha -0.2.9 New MK2.5 and MK3 FW versions -0.2.8 New MK2.5 and MK3 FW version -min_slic3r_version = 1.41.1 -0.2.7 New MK2.5 and MK3 FW version -0.2.6 Added MMU2 MK2.5 settings -min_slic3r_version = 1.41.0-alpha -0.2.5 Prusament is out - added prusament settings -0.2.4 Added soluble support profiles for MMU2 -0.2.3 Added materials for MMU2 single mode, edited MK3 xy stealth feedrate limit -0.2.2 Edited MMU2 Single mode purge line -0.2.1 Added PET and BVOH settings for MMU2 -0.2.0-beta5 Fixed MMU1 ramming parameters -0.2.0-beta4 Added filament loading speed at start, increased minimal purge on wipe tower -0.2.0-beta3 Edited ramming parameters and filament cooling moves for MMU2 -0.2.0-beta2 Edited first layer speed and wipe tower position -0.2.0-beta Removed limit on the MK3MMU2 height, added legacy M204 S T format to the MK2 profiles -0.2.0-alpha8 Added filament_load/unload_time for the PLA/ABS MMU2 filament presets. -0.2.0-alpha7 Vojtech's fix the incorrect *MK3* references -0.2.0-alpha6 Jindra's way to fix the 0.2.0-alpha5 version -0.2.0-alpha5 Bumped up firmware versions for MK2.5/MK3 to 3.3.1, disabled priming areas for MK3MMU2 -0.2.0-alpha4 Extended the custom start/end G-codes of the MMU2.0 printers for no priming towers. -0.2.0-alpha3 Adjusted machine limits for time estimates, added filament density and cost -0.2.0-alpha2 Renamed the key MK3SMMU to MK3MMU2, added a generic PLA MMU2 material -0.2.0-alpha1 added initial profiles for the i3 MK3 Multi Material Upgrade 2.0 -0.2.0-alpha moved machine limits from the start G-code to the new print profile parameters -min_slic3r_version = 1.40.0 -0.1.18 Updated firmware version -0.1.17 Updated firmware version for MK2.5/S and MK3/S -0.1.16 MK2.5/3/S FW update -0.1.15 MK2/S/MMU FW update -0.1.14 Updated firmware versions for MK2.5 and MK3 -0.1.13 New MK2.5 and MK3 FW versions -0.1.12 New MK2.5 and MK3 FW versions -0.1.11 fw version changed to 3.3.1 -0.1.10 MK3 jerk and acceleration update -0.1.9 edited support extrusion width for 0.25 and 0.6 nozzles -0.1.8 extrusion width for 0,25, 0.6 and variable layer height fixes -0.1.7 Fixed errors in 0.25mm and 0.6mm profiles -0.1.6 Split the MK2.5 profile from the MK2S -min_slic3r_version = 1.40.0-beta -0.1.5 fixed printer_variant fields for the i3 MK3 0.25 and 0.6mm nozzles -0.1.4 edited fw version, added z-raise after print -min_slic3r_version = 1.40.0-alpha -0.1.3 Fixed an incorrect position of the max_print_height parameter -0.1.2 Wipe tower changes -0.1.1 Minor print speed adjustments -0.1.0 Initial +min_slic3r_version = 2.4.0-alpha0 +1.3.0-alpha0 Disabled thick bridges, updated support settings. +min_slic3r_version = 2.3.0-rc1 +1.2.4 Updated cost/density values in filament settings. Various changes in print settings. +1.2.3 Updated firmware version. Updated end g-code in MMU2 printer profiles. +1.2.2 Added Prusament PVB filament profile. Added 0.8mm nozzle profiles. +1.2.1 Updated FW version for MK2.5 family printers. +1.2.0 Added full_fan_speed_layer value for PETG. Increased support interface spacing for 0.6mm nozzle profiles. Updated firmware version. +min_slic3r_version = 2.3.0-beta2 +1.2.0-beta1 Updated end g-code. Added full_fan_speed_layer values. +min_slic3r_version = 2.3.0-beta0 +1.2.0-beta0 Adjusted infill anchor limits. Added filament spool weights. +min_slic3r_version = 2.3.0-alpha4 +1.2.0-alpha1 Renamed MK3S and MINI printer profiles. Updated end g-code (MINI). Added new SLA materials and filament profiles. +1.2.0-alpha0 Added filament spool weights +min_slic3r_version = 2.2.0-alpha3 +1.1.13 Updated firmware version. Updated end g-code in MMU2 printer profiles. +1.1.12 Added Prusament PVB filament profile. Added 0.8mm nozzle profiles. +1.1.11 Renamed MK3S and MINI printer profiles. Updated end g-code (MINI). Added new SLA materials and filament profiles. +1.1.10 Updated firmware version. +1.1.9 Updated K values in filament profiles (linear advance). Added new filament profiles and SLA materials. +1.1.8 Updated start/end g-code scripts for MK3 family printer profiles (reduced extruder motor current for some print profiles). Added new filament and SLA material profiles. +1.1.7 Updated end g-code for MMU2 Single printer profiles. Added/updated filament and SLA material profiles. +1.1.6 Updated firmware version for MK2.5/S and MK3/S. +1.1.5 Updated MMU1 specific retraction settings for Prusament PC Blend +1.1.4 Added Prusament PC Blend filament profile. +1.1.3 Added SLA material and filament profile +1.1.2 Added renamed_from fields for PETG filaments to indicate that they were renamed from PET. +1.1.1 Added Verbatim and Fiberlogy PETG filament profiles. Updated auto cooling settings for ABS. +1.1.1-beta Updated for PrusaSlicer 2.2.0-beta +1.1.1-alpha4 Extended list of default filaments to be installed, top/bottom_solid_min_thickness defined, infill_acceleration changed etc +1.1.1-alpha3 Print bed textures are now configurable from the Preset Bundle. Requires PrusaSlicer 2.2.0-alpha3 and newer. +# The following line (max_slic3r_version) forces the users of PrusaSlicer 2.2.0-alpha3 and newer to update the profiles to 1.1.1-alpha3 and newer, +# so they will see the print bed. +max_slic3r_version = 2.2.0-alpha2 +min_slic3r_version = 2.2.0-alpha0 +1.1.1-alpha2 Bumped up config version, so our in house customer will get updated profiles. +1.1.0 Filament aliases, Creality profiles and other goodies for PrusaSlicer 2.2.0-alpha0 +min_slic3r_version = 2.1.1-beta0 +1.0.11 Updated firmware version. +1.0.10 Updated firmware version for MK2.5/S and MK3/S. +1.0.9 Updated firmware version for MK2.5/S and MK3/S. +1.0.8 Various changes in FFF profiles, new filaments/materials added. See changelog. +1.0.7 Updated layer height limits for MINI +1.0.6 Added Prusa MINI profiles +min_slic3r_version = 2.1.0-alpha0 +1.0.5 Added SLA materials +1.0.4 Updated firmware version and 0.25mm nozzle profiles +1.0.3 Added filament profiles +1.0.2 Added SLA materials +1.0.1 Updated MK3 firmware version check to 3.8.0, new soluble support profiles for 0.6mm nozzle diameter MMU2S printers. +1.0.0 Updated end G-code for the MMU2 profiles to lift the extruder at the end of print. Wipe tower bridging distance was made smaller for soluble supports. +1.0.0-beta1 Updated color for the ASA filaments to differ from the other filaments. Single extruder printers now have no extruder color assigned, obects and toolpaths will be colored with the color of the active filament. +1.0.0-beta0 Printer model checks in start G-codes, ASA filament profiles, limits on min / max SL1 exposition times +1.0.0-alpha2 Printer model and nozzle diameter check +1.0.0-alpha1 Added Prusament ASA profile +1.0.0-alpha0 Filament specific retract for PET and similar copolymers, and for FLEX +min_slic3r_version = 1.42.0-alpha6 +0.8.10 Updated firmware version. +0.8.9 Updated firmware version for MK2.5/S and MK3/S. +0.8.8 Updated firmware version for MK2.5/S and MK3/S. +0.8.7 Updated firmware version +0.8.6 Updated firmware version for MK2.5/S and MK3/S +0.8.5 Updated SL1 printer and material settings +0.8.4 Added Prusament ASA profile +0.8.3 FW version and SL1 materials update +0.8.2 FFF and SL1 settings update +0.8.1 Output settings and SLA materials update +0.8.0 Updated for the PrusaSlicer 2.0.0 final release +0.8.0-rc2 Updated firmware versions for MK2.5/S and MK3/S +0.8.0-rc1 Updated SLA profiles +0.8.0-rc Updated for the PrusaSlicer 2.0.0-rc release +0.8.0-beta4 Updated SLA profiles +0.8.0-beta3 Updated SLA profiles +0.8.0-beta2 Updated SLA profiles +0.8.0-beta1 Updated SLA profiles +0.8.0-beta Updated SLA profiles +0.8.0-alpha9 Updated SLA and FFF profiles +0.8.0-alpha8 Updated SLA profiles +0.8.0-alpha7 Updated SLA profiles +0.8.0-alpha6 Updated SLA profiles +min_slic3r_version = 1.42.0-alpha +0.8.0-alpha Updated SLA profiles +0.4.0-alpha4 Updated SLA profiles +0.4.0-alpha3 Update of SLA profiles +0.4.0-alpha2 First SLA profiles +min_slic3r_version = 1.41.3-alpha +0.4.12 Updated firmware version for MK2.5/S and MK3/S. +0.4.11 Updated firmware version for MK2.5/S and MK3/S. +0.4.10 Updated firmware version +0.4.9 Updated firmware version for MK2.5/S and MK3/S +0.4.8 MK2.5/3/S FW update +0.4.7 MK2/S/MMU FW update +0.4.6 Updated firmware versions for MK2.5/S and MK3/S +0.4.5 Enabled remaining time support for MK2/S/MMU1 +0.4.4 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt +0.4.3 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt +0.4.2 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt +0.4.1 New MK2.5S and MK3S FW versions +0.4.0 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt +min_slic3r_version = 1.41.1 +0.3.11 Updated firmware version for MK2.5/S and MK3/S. +0.3.10 Updated firmware version +0.3.9 Updated firmware version for MK2.5/S and MK3/S +0.3.8 MK2.5/3/S FW update +0.3.7 MK2/S/MMU FW update +0.3.6 Updated firmware versions for MK2.5 and MK3 +0.3.5 New MK2.5 and MK3 FW versions +0.3.4 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt +0.3.3 Prusament PETG released +0.3.2 New MK2.5 and MK3 FW versions +0.3.1 New MK2.5 and MK3 FW versions +0.3.0 New MK2.5 and MK3 FW version +min_slic3r_version = 1.41.0-alpha +0.2.9 New MK2.5 and MK3 FW versions +0.2.8 New MK2.5 and MK3 FW version +min_slic3r_version = 1.41.1 +0.2.7 New MK2.5 and MK3 FW version +0.2.6 Added MMU2 MK2.5 settings +min_slic3r_version = 1.41.0-alpha +0.2.5 Prusament is out - added prusament settings +0.2.4 Added soluble support profiles for MMU2 +0.2.3 Added materials for MMU2 single mode, edited MK3 xy stealth feedrate limit +0.2.2 Edited MMU2 Single mode purge line +0.2.1 Added PET and BVOH settings for MMU2 +0.2.0-beta5 Fixed MMU1 ramming parameters +0.2.0-beta4 Added filament loading speed at start, increased minimal purge on wipe tower +0.2.0-beta3 Edited ramming parameters and filament cooling moves for MMU2 +0.2.0-beta2 Edited first layer speed and wipe tower position +0.2.0-beta Removed limit on the MK3MMU2 height, added legacy M204 S T format to the MK2 profiles +0.2.0-alpha8 Added filament_load/unload_time for the PLA/ABS MMU2 filament presets. +0.2.0-alpha7 Vojtech's fix the incorrect *MK3* references +0.2.0-alpha6 Jindra's way to fix the 0.2.0-alpha5 version +0.2.0-alpha5 Bumped up firmware versions for MK2.5/MK3 to 3.3.1, disabled priming areas for MK3MMU2 +0.2.0-alpha4 Extended the custom start/end G-codes of the MMU2.0 printers for no priming towers. +0.2.0-alpha3 Adjusted machine limits for time estimates, added filament density and cost +0.2.0-alpha2 Renamed the key MK3SMMU to MK3MMU2, added a generic PLA MMU2 material +0.2.0-alpha1 added initial profiles for the i3 MK3 Multi Material Upgrade 2.0 +0.2.0-alpha moved machine limits from the start G-code to the new print profile parameters +min_slic3r_version = 1.40.0 +0.1.18 Updated firmware version +0.1.17 Updated firmware version for MK2.5/S and MK3/S +0.1.16 MK2.5/3/S FW update +0.1.15 MK2/S/MMU FW update +0.1.14 Updated firmware versions for MK2.5 and MK3 +0.1.13 New MK2.5 and MK3 FW versions +0.1.12 New MK2.5 and MK3 FW versions +0.1.11 fw version changed to 3.3.1 +0.1.10 MK3 jerk and acceleration update +0.1.9 edited support extrusion width for 0.25 and 0.6 nozzles +0.1.8 extrusion width for 0,25, 0.6 and variable layer height fixes +0.1.7 Fixed errors in 0.25mm and 0.6mm profiles +0.1.6 Split the MK2.5 profile from the MK2S +min_slic3r_version = 1.40.0-beta +0.1.5 fixed printer_variant fields for the i3 MK3 0.25 and 0.6mm nozzles +0.1.4 edited fw version, added z-raise after print +min_slic3r_version = 1.40.0-alpha +0.1.3 Fixed an incorrect position of the max_print_height parameter +0.1.2 Wipe tower changes +0.1.1 Minor print speed adjustments +0.1.0 Initial diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index 796d578226..16737d1563 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -5,18 +5,15 @@ name = Prusa Research # Configuration version of this file. Config file will only be installed, if the config_version differs. # This means, the server may force the PrusaSlicer configuration to be downgraded. -config_version = 1.2.4 +config_version = 1.3.0-alpha0 # Where to get the updates from? config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/ changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1% # The printer models will be shown by the Configuration Wizard in this order, # also the first model installed & the first nozzle installed will be activated after install. -#TODO: One day we may differentiate variants of the nozzles / hot ends, -#for example by the melt zone size, or whether the nozzle is hardened. # Printer model name will be shown by the installation wizard. - [printer_model:MINI] name = Original Prusa MINI && MINI+ variants = 0.4; 0.25; 0.6; 0.8 @@ -128,20 +125,21 @@ default_materials = Prusa Orange Tough @0.05 # All presets starting with asterisk, for example *common*, are intermediate and they will # not make it into the user interface. -# Common print preset, mostly derived from MK2 single material with a 0.4mm nozzle. -# All other print presets will derive from the *common* print preset. +# Common print presets + [print:*common*] avoid_crossing_perimeters = 0 +thick_bridges = 0 bridge_acceleration = 1000 bridge_angle = 0 -bridge_flow_ratio = 0.8 -bridge_speed = 20 +bridge_flow_ratio = 1 +bridge_speed = 25 brim_width = 0 clip_multipart_objects = 1 compatible_printers = complete_objects = 0 default_acceleration = 1000 -dont_support_bridges = 1 +dont_support_bridges = 0 elefant_foot_compensation = 0.2 ensure_vertical_shell_thickness = 1 external_fill_pattern = rectilinear @@ -154,7 +152,7 @@ extrusion_width = 0.45 fill_angle = 45 fill_density = 20% fill_pattern = cubic -first_layer_acceleration = 1000 +first_layer_acceleration = 800 first_layer_extrusion_width = 0.42 first_layer_height = 0.2 first_layer_speed = 20 @@ -183,6 +181,7 @@ perimeter_extrusion_width = 0.45 post_process = print_settings_id = raft_layers = 0 +raft_first_layer_density = 90% resolution = 0 seam_position = nearest single_extruder_multi_material_priming = 1 @@ -203,7 +202,7 @@ support_material_interface_extruder = 0 support_material_angle = 0 support_material_buildplate_only = 0 support_material_enforce_layers = 0 -support_material_contact_distance = 0.1 +support_material_contact_distance = 0.2 support_material_interface_contact_loops = 0 support_material_interface_layers = 2 support_material_interface_spacing = 0.2 @@ -212,9 +211,10 @@ support_material_pattern = rectilinear support_material_spacing = 2 support_material_speed = 50 support_material_synchronize_layers = 0 -support_material_threshold = 55 +support_material_threshold = 50 support_material_with_sheath = 0 -support_material_xy_spacing = 50% +support_material_xy_spacing = 60% +support_material_bottom_interface_layers = 0 thin_walls = 0 top_infill_extrusion_width = 0.45 top_solid_infill_speed = 40 @@ -240,20 +240,15 @@ wipe_tower_x = 170 wipe_tower_y = 125 [print:*MK306*] +inherits = *MK3* fill_pattern = gyroid fill_density = 15% -single_extruder_multi_material_priming = 0 -travel_speed = 180 -wipe_tower_x = 170 -wipe_tower_y = 125 - -## MINI [print:*MINI*] fill_pattern = grid travel_speed = 150 wipe_tower = 0 -default_acceleration = 1250 +default_acceleration = 1000 first_layer_acceleration = 800 infill_acceleration = 1000 bridge_acceleration = 1000 @@ -262,7 +257,6 @@ max_print_speed = 150 extruder_clearance_height = 20 extruder_clearance_radius = 35 -# Print parameters common to a 0.25mm diameter nozzle. [print:*0.25nozzle*] elefant_foot_compensation = 0 external_perimeter_extrusion_width = 0.25 @@ -277,7 +271,11 @@ support_material_interface_layers = 0 support_material_interface_spacing = 0.15 support_material_spacing = 1 support_material_xy_spacing = 150% +support_material_contact_distance = 0.1 output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode +thick_bridges = 0 +bridge_flow_ratio = 1 +bridge_speed = 20 [print:*0.25nozzleMK3*] inherits = *0.25nozzle* @@ -288,7 +286,6 @@ infill_speed = 45 solid_infill_speed = 45 top_solid_infill_speed = 30 support_material_speed = 40 -bridge_speed = 20 gap_fill_speed = 30 perimeter_acceleration = 500 infill_acceleration = 1000 @@ -307,7 +304,6 @@ solid_infill_speed = 40 infill_acceleration = 800 first_layer_acceleration = 500 -# Print parameters common to a 0.6mm diameter nozzle. [print:*0.6nozzle*] external_perimeter_extrusion_width = 0.61 extrusion_width = 0.67 @@ -324,6 +320,31 @@ output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_heig infill_anchor_max = 15 top_solid_min_thickness = 0.9 bottom_solid_min_thickness = 0.6 +thick_bridges = 1 +bridge_flow_ratio = 0.95 +bridge_speed = 25 + +[print:*0.6nozzleMK3*] +inherits = *0.6nozzle* +external_perimeter_extrusion_width = 0.65 +extrusion_width = 0.65 +infill_extrusion_width = 0.65 +thick_bridges = 0 + +[print:*0.6nozzleMINI*] +inherits = *0.6nozzleMK3* +infill_extrusion_width = 0.68 +solid_infill_extrusion_width = 0.68 +fill_pattern = gyroid +fill_density = 15% +travel_speed = 150 +perimeter_acceleration = 800 +infill_acceleration = 1000 +bridge_acceleration = 1000 +first_layer_acceleration = 800 +default_acceleration = 1250 +support_material_speed = 40 +support_material_interface_speed = 100% [print:*0.8nozzle*] external_perimeter_extrusion_width = 0.9 @@ -356,34 +377,12 @@ bridge_flow_ratio = 0.9 perimeter_acceleration = 800 infill_acceleration = 1000 bridge_acceleration = 1000 -first_layer_acceleration = 1000 +first_layer_acceleration = 800 default_acceleration = 1000 top_solid_min_thickness = 1.2 bottom_solid_min_thickness = 0.8 single_extruder_multi_material_priming = 0 - -[print:*0.6nozzleMK3*] -inherits = *0.6nozzle* -external_perimeter_extrusion_width = 0.65 -extrusion_width = 0.65 -infill_extrusion_width = 0.65 -bridge_flow_ratio = 0.95 -bridge_speed = 25 - -[print:*0.6nozzleMINI*] -inherits = *0.6nozzleMK3* -infill_extrusion_width = 0.68 -solid_infill_extrusion_width = 0.68 -fill_pattern = gyroid -fill_density = 15% -travel_speed = 150 -perimeter_acceleration = 800 -infill_acceleration = 1000 -bridge_acceleration = 1000 -first_layer_acceleration = 1000 -default_acceleration = 1250 -support_material_speed = 40 -support_material_interface_speed = 100% +thick_bridges = 1 [print:*soluble_support*] overhangs = 1 @@ -399,29 +398,29 @@ support_material_threshold = 80 support_material_with_sheath = 1 wipe_tower_bridging = 6 support_material_interface_speed = 80% - -# XXXXXXXXXXXXXXXXXXXX -# XXX--- 0.05mm ---XXX -# XXXXXXXXXXXXXXXXXXXX +support_material_bottom_interface_layers = -1 +thick_bridges = 1 [print:*0.05mm*] inherits = *common* +layer_height = 0.05 bottom_solid_layers = 10 bridge_acceleration = 300 -bridge_flow_ratio = 0.7 +bridge_flow_ratio = 1.15 +bridge_speed = 15 default_acceleration = 1000 external_perimeter_speed = 20 fill_density = 20% -first_layer_acceleration = 500 +first_layer_acceleration = 800 gap_fill_speed = 20 infill_acceleration = 800 infill_speed = 30 max_print_speed = 80 small_perimeter_speed = 20 solid_infill_speed = 30 -support_material_extrusion_width = 0.3 +support_material_extrusion_width = 0.33 support_material_spacing = 1.5 -layer_height = 0.05 +support_material_contact_distance = 0.15 perimeter_acceleration = 300 perimeter_speed = 30 perimeters = 3 @@ -429,151 +428,28 @@ support_material_speed = 30 top_solid_infill_speed = 20 top_solid_layers = 15 -[print:0.05mm ULTRADETAIL] -inherits = *0.05mm* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.4 and num_extruders==1 -infill_extrusion_width = 0.5 - -# MK3 # -[print:0.05mm ULTRADETAIL @MK3] -inherits = *0.05mm*; *MK3* -fill_pattern = gyroid -fill_density = 15% -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and ! single_extruder_multi_material -top_infill_extrusion_width = 0.4 - -# MK2 # -[print:0.05mm ULTRADETAIL @0.25 nozzle] -inherits = *0.05mm*; *0.25nozzle* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.25 and num_extruders==1 -fill_density = 20% -infill_speed = 20 -max_print_speed = 100 -perimeter_speed = 20 -small_perimeter_speed = 15 -solid_infill_speed = 20 -support_material_speed = 20 - -# MK3 # -[print:0.05mm ULTRADETAIL @0.25 nozzle MK3] -inherits = *0.05mm*; *0.25nozzle*; *MK3* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.25 and num_extruders==1 -fill_pattern = grid -fill_density = 20% - -# XXXXXXXXXXXXXXXXXXXX -# XXX--- 0.07mm ---XXX -# XXXXXXXXXXXXXXXXXXXX - [print:*0.07mm*] -inherits = *common* -bottom_solid_layers = 8 -bridge_acceleration = 300 -bridge_flow_ratio = 0.7 -bridge_speed = 20 -default_acceleration = 1000 -external_perimeter_speed = 20 -fill_density = 15% -first_layer_acceleration = 500 -gap_fill_speed = 20 -infill_acceleration = 800 -infill_speed = 40 -max_print_speed = 80 -small_perimeter_speed = 20 -solid_infill_speed = 40 -support_material_extrusion_width = 0.3 -support_material_spacing = 1.5 +inherits = *0.05mm* layer_height = 0.07 -perimeter_acceleration = 300 -perimeter_speed = 30 -perimeters = 3 +bottom_solid_layers = 8 +bridge_flow_ratio = 1 +fill_density = 15% +infill_speed = 40 +solid_infill_speed = 40 support_material_speed = 40 top_solid_infill_speed = 30 top_solid_layers = 11 -# MK3 # -[print:0.07mm ULTRADETAIL @MK3] -inherits = *0.07mm*; *MK3* -fill_pattern = gyroid -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and ! single_extruder_multi_material -top_infill_extrusion_width = 0.4 - -[print:0.07mm ULTRADETAIL @0.25 nozzle MK3] -inherits = *0.07mm*; *0.25nozzle*; *MK3* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.25 and num_extruders==1 -infill_speed = 30 -solid_infill_speed = 30 -support_material_speed = 30 -top_solid_infill_speed = 20 -fill_pattern = grid -fill_density = 20% - -# XXXXXXXXXXXXXXXXXXXX -# XXX--- 0.10mm ---XXX -# XXXXXXXXXXXXXXXXXXXX - -# MK2 # [print:*0.10mm*] inherits = *common* bottom_solid_layers = 7 -bridge_flow_ratio = 0.7 +bridge_flow_ratio = 1 +bridge_speed = 20 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.4 and num_extruders==1 layer_height = 0.1 perimeter_acceleration = 800 top_solid_layers = 9 - -# MK2 # -[print:0.10mm DETAIL] -inherits = *0.10mm* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.4 and num_extruders==1 -external_perimeter_speed = 40 -infill_acceleration = 2000 -infill_speed = 60 -perimeter_speed = 50 -solid_infill_speed = 50 -perimeters = 3 - -# MK3 # -[print:0.10mm DETAIL @MK3] -inherits = *0.10mm*; *MK3* -bridge_speed = 30 -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 -external_perimeter_speed = 25 -infill_acceleration = 1000 -infill_speed = 80 -max_print_speed = 200 -perimeter_speed = 45 -solid_infill_speed = 80 -top_infill_extrusion_width = 0.4 -top_solid_infill_speed = 40 -fill_pattern = gyroid -fill_density = 15% -perimeters = 3 - -# MK2 # -[print:0.10mm DETAIL @0.25 nozzle] -inherits = *0.10mm*; *0.25nozzle* -bridge_acceleration = 600 -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.25 -external_perimeter_speed = 20 -infill_acceleration = 1000 -infill_speed = 40 -perimeter_acceleration = 600 -perimeter_speed = 25 -small_perimeter_speed = 15 -solid_infill_speed = 40 -top_solid_infill_speed = 30 - -# MK3 # -[print:0.10mm DETAIL @0.25 nozzle MK3] -inherits = *0.10mm*; *0.25nozzleMK3*; *MK3* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.25 -fill_pattern = grid -fill_density = 20% - -# XXXXXXXXXXXXXXXXXXXX -# XXX--- 0.15mm ---XXX -# XXXXXXXXXXXXXXXXXXXX +support_material_contact_distance = 0.17 [print:*0.15mm*] inherits = *common* @@ -587,138 +463,8 @@ perimeter_speed = 50 solid_infill_speed = 50 top_infill_extrusion_width = 0.4 top_solid_layers = 7 - -# MK2 # -[print:0.15mm 100mms Linear Advance] -inherits = *0.15mm* -bridge_flow_ratio = 0.95 -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 -external_perimeter_speed = 50 -infill_speed = 100 -max_print_speed = 150 -perimeter_speed = 60 -small_perimeter_speed = 30 -solid_infill_speed = 100 -support_material_speed = 60 -top_solid_infill_speed = 70 - -# MK2 # -[print:0.15mm OPTIMAL] -inherits = *0.15mm* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 -top_infill_extrusion_width = 0.45 - -# MK2 # -[print:0.15mm OPTIMAL @0.25 nozzle] -inherits = *0.15mm*; *0.25nozzle* -bridge_acceleration = 600 -bridge_flow_ratio = 0.7 -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.25 -external_perimeter_speed = 20 -infill_acceleration = 1000 -infill_speed = 40 -perimeter_acceleration = 600 -perimeter_speed = 25 -small_perimeter_speed = 15 -solid_infill_speed = 40 -top_solid_infill_speed = 30 - -# MK2 # -[print:0.15mm OPTIMAL @0.6 nozzle] -inherits = *0.15mm*; *0.6nozzle* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.6 - -# MK3 # -[print:0.15mm QUALITY @MK3] -inherits = *0.15mm*; *MK3* -bridge_speed = 30 -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 -external_perimeter_speed = 25 -infill_acceleration = 1000 -infill_speed = 80 -max_print_speed = 200 -perimeter_speed = 45 -solid_infill_speed = 80 -top_solid_infill_speed = 40 -fill_pattern = gyroid -fill_density = 15% - -[print:0.15mm SPEED @MK3] -inherits = *0.15mm*; *MK3* -bridge_speed = 30 -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 -external_perimeter_speed = 35 -infill_acceleration = 1000 -infill_speed = 200 -max_print_speed = 200 -perimeter_speed = 60 -solid_infill_speed = 200 -top_solid_infill_speed = 50 - -# MK3 MMU # -[print:0.15mm SOLUBLE FULL @MK3] -inherits = 0.15mm SPEED @MK3; *soluble_support* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and num_extruders>1 -notes = Set your soluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder -support_material_extruder = 5 -support_material_interface_extruder = 5 -perimeter_speed = 40 -solid_infill_speed = 40 -infill_speed = 80 -top_infill_extrusion_width = 0.45 -top_solid_infill_speed = 30 -support_material_speed = 45 - -# MK3 MMU # -[print:0.15mm SOLUBLE INTERFACE @MK3] -inherits = 0.15mm SOLUBLE FULL @MK3 -notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder -support_material_extruder = 0 -support_material_interface_layers = 3 -support_material_with_sheath = 0 - -# MK2 MMU # -[print:0.15mm OPTIMAL SOLUBLE FULL] -inherits = *0.15mm*; *soluble_support* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 and num_extruders>1 -external_perimeter_speed = 25 -notes = Set your soluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder -perimeter_speed = 40 -solid_infill_speed = 40 -top_infill_extrusion_width = 0.45 -top_solid_infill_speed = 30 - -# MK2 MMU # -[print:0.15mm OPTIMAL SOLUBLE INTERFACE] -inherits = 0.15mm OPTIMAL SOLUBLE FULL -notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder -support_material_extruder = 0 -support_material_interface_layers = 3 -support_material_with_sheath = 0 -support_material_xy_spacing = 80% - -# MK3 # -[print:0.15mm QUALITY @0.25 nozzle MK3] -inherits = *0.15mm*; *0.25nozzleMK3*; *MK3* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.25 -fill_pattern = grid -fill_density = 20% - -# MK3 # -[print:0.15mm DETAIL @0.6 nozzle MK3] -inherits = *0.15mm*; *0.6nozzleMK3*; *MK306* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6 -external_perimeter_speed = 35 -infill_acceleration = 1000 -infill_speed = 70 -max_print_speed = 100 -perimeter_speed = 45 -solid_infill_speed = 70 -top_solid_infill_speed = 45 - -# XXXXXXXXXXXXXXXXXXXX -# XXX--- 0.20mm ---XXX -# XXXXXXXXXXXXXXXXXXXX +bridge_flow_ratio = 1 +bridge_speed = 25 [print:*0.20mm*] inherits = *common* @@ -734,114 +480,6 @@ solid_infill_speed = 50 top_infill_extrusion_width = 0.4 top_solid_layers = 5 -# MK2 # -[print:0.20mm 100mms Linear Advance] -inherits = *0.20mm* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 -external_perimeter_speed = 50 -infill_speed = 100 -max_print_speed = 150 -perimeter_speed = 60 -small_perimeter_speed = 30 -solid_infill_speed = 100 -support_material_speed = 60 -top_solid_infill_speed = 70 - -# MK3 # -[print:0.20mm QUALITY @MK3] -inherits = *0.20mm*; *MK3* -bridge_speed = 30 -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 -external_perimeter_speed = 25 -infill_acceleration = 1000 -infill_speed = 80 -max_print_speed = 200 -perimeter_speed = 45 -solid_infill_speed = 80 -top_solid_infill_speed = 40 -fill_pattern = gyroid -fill_density = 15% - -[print:0.20mm SPEED @MK3] -inherits = *0.20mm*; *MK3* -bridge_speed = 30 -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 -external_perimeter_speed = 35 -infill_acceleration = 1000 -infill_speed = 200 -max_print_speed = 200 -perimeter_speed = 60 -solid_infill_speed = 200 -top_solid_infill_speed = 50 - -# MK3 MMU # -[print:0.20mm SOLUBLE FULL @MK3] -inherits = 0.20mm SPEED @MK3; *soluble_support* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and num_extruders>1 -notes = Set your soluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder -support_material_extruder = 5 -support_material_interface_extruder = 5 -perimeter_speed = 40 -solid_infill_speed = 40 -infill_speed = 80 -top_infill_extrusion_width = 0.45 -top_solid_infill_speed = 30 -support_material_speed = 45 - -# MK3 MMU # -[print:0.20mm SOLUBLE INTERFACE @MK3] -inherits = 0.20mm SOLUBLE FULL @MK3 -notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder -support_material_extruder = 0 -support_material_interface_layers = 3 -support_material_with_sheath = 0 - -# MK2 # -[print:0.20mm NORMAL] -inherits = *0.20mm* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 - -# MK2 # -[print:0.20mm NORMAL @0.6 nozzle] -inherits = *0.20mm*; *0.6nozzle* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.6 - -# MK2 MMU # -[print:0.20mm NORMAL SOLUBLE FULL] -inherits = *0.20mm*; *soluble_support* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 and num_extruders>1 -external_perimeter_speed = 30 -notes = Set your soluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder -perimeter_speed = 40 -solid_infill_speed = 40 -top_solid_infill_speed = 30 - -# MK2 MMU # -[print:0.20mm NORMAL SOLUBLE INTERFACE] -inherits = 0.20mm NORMAL SOLUBLE FULL -notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder -support_material_extruder = 0 -support_material_interface_layers = 3 -support_material_with_sheath = 0 -support_material_xy_spacing = 80% - -# MK3 # -[print:0.20mm DETAIL @0.6 nozzle MK3] -inherits = *0.20mm*; *0.6nozzleMK3*; *MK306* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6 -external_perimeter_speed = 35 -infill_acceleration = 1000 -infill_speed = 70 -max_print_speed = 100 -perimeter_speed = 45 -solid_infill_speed = 70 -top_solid_infill_speed = 45 - - -# XXXXXXXXXXXXXXXXXXXX -# XXX--- 0.25mm ---XXX -# XXXXXXXXXXXXXXXXXXXX - [print:*0.25mm*] inherits = *common* bottom_solid_layers = 4 @@ -852,10 +490,6 @@ layer_height = 0.25 perimeter_speed = 50 top_solid_layers = 4 -# XXXXXXXXXXXXXXXXXXXX -# XXX--- 0.30mm ---XXX -# XXXXXXXXXXXXXXXXXXXX - [print:*0.30mm*] inherits = *common* bottom_solid_layers = 4 @@ -869,65 +503,7 @@ perimeter_speed = 50 solid_infill_speed = 50 top_infill_extrusion_width = 0.4 top_solid_layers = 4 - -[print:0.30mm QUALITY @0.6 nozzle MK3] -inherits = *0.30mm*; *0.6nozzleMK3*; *MK306* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6 -external_perimeter_speed = 35 -infill_acceleration = 1000 -infill_speed = 70 -max_print_speed = 100 -perimeter_speed = 45 -solid_infill_speed = 70 -top_solid_infill_speed = 45 - -[print:0.30mm SOLUBLE FULL @0.6 nozzle MK3] -inherits = 0.30mm QUALITY @0.6 nozzle MK3; *soluble_support* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6 and num_extruders>1 -notes = Set your soluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder -support_material_extruder = 5 -support_material_interface_extruder = 5 -support_material_speed = 40 -perimeter_speed = 40 -solid_infill_speed = 40 -top_infill_extrusion_width = 0.6 -support_material_extrusion_width = 0.6 -top_solid_infill_speed = 30 -support_material_xy_spacing = 80% - -[print:0.30mm SOLUBLE INTERFACE @0.6 nozzle MK3] -inherits = 0.30mm SOLUBLE FULL @0.6 nozzle MK3 -notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder -support_material_extruder = 0 -support_material_interface_layers = 3 -support_material_with_sheath = 0 - -[print:0.30mm DRAFT @MK3] -inherits = *0.30mm*; *MK3* -bottom_solid_layers = 3 -bridge_speed = 30 -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 -external_perimeter_speed = 35 -infill_acceleration = 1000 -infill_speed = 85 -max_print_speed = 200 -perimeter_speed = 50 -small_perimeter_speed = 30 -solid_infill_speed = 80 -top_solid_infill_speed = 40 -support_material_speed = 45 -external_perimeter_extrusion_width = 0.6 -extrusion_width = 0.5 -first_layer_extrusion_width = 0.42 -infill_extrusion_width = 0.5 -perimeter_extrusion_width = 0.5 -solid_infill_extrusion_width = 0.5 -top_infill_extrusion_width = 0.45 -support_material_extrusion_width = 0.38 - -# XXXXXXXXXXXXXXXXXXXX -# XXX--- 0.35mm ---XXX -# XXXXXXXXXXXXXXXXXXXX +support_material_contact_distance = 0.3 [print:*0.35mm*] inherits = *common* @@ -946,65 +522,6 @@ solid_infill_speed = 60 top_solid_infill_speed = 50 top_solid_layers = 4 -# MK2 # -[print:0.35mm FAST] -inherits = *0.35mm* -bridge_flow_ratio = 0.95 -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 -first_layer_extrusion_width = 0.42 -perimeter_extrusion_width = 0.43 -solid_infill_extrusion_width = 0.7 -top_infill_extrusion_width = 0.43 -support_material_extrusion_width = 0.37 - -# MK2 # -[print:0.35mm FAST @0.6 nozzle] -inherits = *0.35mm*; *0.6nozzle* -# alias = 0.35mm FAST -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.6 - -# MK2 MMU # -[print:0.35mm FAST sol full @0.6 nozzle] -inherits = *0.35mm*; *0.6nozzle*; *soluble_support* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_model=="MK2SMM" and nozzle_diameter[0]==0.6 and num_extruders>1 -external_perimeter_extrusion_width = 0.6 -external_perimeter_speed = 30 -notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder -perimeter_speed = 40 -support_material_speed = 40 -support_material_interface_layers = 2 -support_material_xy_spacing = 120% -top_infill_extrusion_width = 0.6 -support_material_extrusion_width = 0.6 - -# MK2 MMU # -[print:0.35mm FAST sol int @0.6 nozzle] -inherits = 0.35mm FAST sol full @0.6 nozzle -support_material_extruder = 0 -support_material_interface_layers = 3 -support_material_with_sheath = 0 -support_material_xy_spacing = 150% - -# MK3 # -[print:0.35mm SPEED @0.6 nozzle MK3] -inherits = *0.35mm*; *0.6nozzleMK3*; *MK306* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6 -external_perimeter_speed = 35 -infill_acceleration = 1000 -infill_speed = 70 -max_print_speed = 100 -perimeter_speed = 45 -solid_infill_speed = 70 -top_solid_infill_speed = 45 -external_perimeter_extrusion_width = 0.68 -perimeter_extrusion_width = 0.68 -infill_extrusion_width = 0.68 -solid_infill_extrusion_width = 0.68 - -# XXXXXXXXXXXXXXXXXXXX -# XXX--- 0.40mm ---XXX -# XXXXXXXXXXXXXXXXXXXX - [print:*0.40mm*] inherits = *common* bottom_solid_layers = 3 @@ -1022,71 +539,236 @@ solid_infill_speed = 60 top_solid_infill_speed = 40 top_solid_layers = 4 -# MK3 # -[print:0.40mm DRAFT @0.6 nozzle MK3] -inherits = *0.40mm*; *0.6nozzleMK3*; *MK306* -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6 -external_perimeter_speed = 35 -infill_acceleration = 1000 -infill_speed = 70 +## MK2 family ## + +## MK2 - 0.4mm nozzle +[print:0.05mm ULTRADETAIL] +inherits = *0.05mm* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.4 and num_extruders==1 +infill_extrusion_width = 0.5 + +[print:0.10mm DETAIL] +inherits = *0.10mm* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.4 and num_extruders==1 +external_perimeter_speed = 40 +infill_acceleration = 2000 +infill_speed = 60 +perimeter_speed = 50 +solid_infill_speed = 50 +perimeters = 3 +bridge_acceleration = 800 + +[print:0.15mm 100mms Linear Advance] +inherits = *0.15mm* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 +external_perimeter_speed = 50 +infill_speed = 100 +max_print_speed = 150 +perimeter_speed = 60 +small_perimeter_speed = 30 +solid_infill_speed = 100 +support_material_speed = 60 +top_solid_infill_speed = 70 + +[print:0.15mm OPTIMAL] +inherits = *0.15mm* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 +top_infill_extrusion_width = 0.45 + +[print:0.20mm 100mms Linear Advance] +inherits = *0.20mm* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 +external_perimeter_speed = 50 +infill_speed = 100 +max_print_speed = 150 +perimeter_speed = 60 +small_perimeter_speed = 30 +solid_infill_speed = 100 +support_material_speed = 60 +top_solid_infill_speed = 70 + +[print:0.20mm NORMAL] +inherits = *0.20mm* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 + +[print:0.35mm FAST] +inherits = *0.35mm* +bridge_flow_ratio = 0.95 +bridge_speed = 30 +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 +first_layer_extrusion_width = 0.42 +perimeter_extrusion_width = 0.43 +solid_infill_extrusion_width = 0.7 +top_infill_extrusion_width = 0.45 +support_material_extrusion_width = 0.37 +support_material_contact_distance = 0.1 +top_solid_infill_speed = 40 +thick_bridges = 1 + +## MMU1 specific +[print:0.15mm OPTIMAL SOLUBLE FULL] +inherits = *0.15mm*; *soluble_support* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 and num_extruders>1 +external_perimeter_speed = 25 +notes = Set your soluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder +perimeter_speed = 40 +solid_infill_speed = 40 +top_infill_extrusion_width = 0.45 +top_solid_infill_speed = 30 +bridge_flow_ratio = 0.8 +bridge_speed = 30 + +[print:0.15mm OPTIMAL SOLUBLE INTERFACE] +inherits = 0.15mm OPTIMAL SOLUBLE FULL +notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder +support_material_extruder = 0 +support_material_interface_layers = 3 +support_material_with_sheath = 0 +support_material_xy_spacing = 80% + +[print:0.20mm NORMAL SOLUBLE FULL] +inherits = *0.20mm*; *soluble_support* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 and num_extruders>1 +external_perimeter_speed = 30 +notes = Set your soluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder +perimeter_speed = 40 +solid_infill_speed = 40 +top_solid_infill_speed = 30 +bridge_flow_ratio = 0.95 +bridge_speed = 30 + +[print:0.20mm NORMAL SOLUBLE INTERFACE] +inherits = 0.20mm NORMAL SOLUBLE FULL +notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder +support_material_extruder = 0 +support_material_interface_layers = 3 +support_material_with_sheath = 0 +support_material_xy_spacing = 80% + +## MK2 - 0.25mm nozzle + +[print:0.05mm ULTRADETAIL @0.25 nozzle] +inherits = *0.05mm*; *0.25nozzle* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.25 and num_extruders==1 +fill_density = 20% +infill_speed = 20 max_print_speed = 100 -perimeter_speed = 45 -solid_infill_speed = 70 -top_solid_infill_speed = 45 -external_perimeter_extrusion_width = 0.68 -perimeter_extrusion_width = 0.68 -infill_extrusion_width = 0.68 -solid_infill_extrusion_width = 0.68 +perimeter_speed = 20 +small_perimeter_speed = 15 +solid_infill_speed = 20 +support_material_speed = 20 +support_material_contact_distance = 0.07 -# XXXXXXXXXXXXXXXXXXXXXX -# XXX----- MK2.5 ----XXX -# XXXXXXXXXXXXXXXXXXXXXX +[print:0.10mm DETAIL @0.25 nozzle] +inherits = *0.10mm*; *0.25nozzle* +bridge_acceleration = 600 +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.25 +external_perimeter_speed = 20 +infill_acceleration = 1000 +infill_speed = 40 +perimeter_acceleration = 600 +perimeter_speed = 25 +small_perimeter_speed = 15 +solid_infill_speed = 40 +top_solid_infill_speed = 30 +support_material_contact_distance = 0.07 -# MK2.5 # -[print:0.15mm 100mms Linear Advance @MK2.5] -inherits = 0.15mm 100mms Linear Advance -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 -single_extruder_multi_material_priming = 0 +[print:0.15mm OPTIMAL @0.25 nozzle] +inherits = *0.15mm*; *0.25nozzle* +bridge_acceleration = 600 +bridge_flow_ratio = 0.8 +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.25 +external_perimeter_speed = 20 +infill_acceleration = 1000 +infill_speed = 40 +perimeter_acceleration = 600 +perimeter_speed = 25 +small_perimeter_speed = 15 +solid_infill_speed = 40 +top_solid_infill_speed = 30 +support_material_contact_distance = 0.08 -# MK2.5 # -[print:0.15mm OPTIMAL @MK2.5] -inherits = 0.15mm OPTIMAL -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 -single_extruder_multi_material_priming = 0 +## MK2 - 0.6mm nozzle + +[print:0.15mm OPTIMAL @0.6 nozzle] +inherits = *0.15mm*; *0.6nozzle* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.6 + +[print:0.20mm NORMAL @0.6 nozzle] +inherits = *0.20mm*; *0.6nozzle* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.6 + +[print:0.35mm FAST @0.6 nozzle] +inherits = *0.35mm*; *0.6nozzle* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.6 + +## MMU1 specific +[print:0.35mm FAST sol full @0.6 nozzle] +inherits = *0.35mm*; *0.6nozzle*; *soluble_support* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_model=="MK2SMM" and nozzle_diameter[0]==0.6 and num_extruders>1 +external_perimeter_extrusion_width = 0.6 +external_perimeter_speed = 30 +notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder +perimeter_speed = 40 +support_material_speed = 40 +support_material_interface_layers = 2 +support_material_xy_spacing = 120% +top_infill_extrusion_width = 0.6 +support_material_extrusion_width = 0.6 + +[print:0.35mm FAST sol int @0.6 nozzle] +inherits = 0.35mm FAST sol full @0.6 nozzle +support_material_extruder = 0 +support_material_interface_layers = 3 +support_material_with_sheath = 0 +support_material_xy_spacing = 150% + +## MK2.5 -# MK2.5 MMU2 # [print:0.10mm DETAIL @MK2.5] inherits = 0.10mm DETAIL compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 and num_extruders>1 single_extruder_multi_material_priming = 0 -# MK2.5 MMU2 # +[print:0.15mm 100mms Linear Advance @MK2.5] +inherits = 0.15mm 100mms Linear Advance +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 +single_extruder_multi_material_priming = 0 + +[print:0.15mm OPTIMAL @MK2.5] +inherits = 0.15mm OPTIMAL +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 +single_extruder_multi_material_priming = 0 + +[print:0.20mm 100mms Linear Advance @MK2.5] +inherits = 0.20mm 100mms Linear Advance +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 +single_extruder_multi_material_priming = 0 + +[print:0.20mm NORMAL @MK2.5] +inherits = 0.20mm NORMAL +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 +single_extruder_multi_material_priming = 0 + +[print:0.35mm FAST @MK2.5] +inherits = 0.35mm FAST +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 +single_extruder_multi_material_priming = 0 + +## MK2.5 - MMU2 specific + [print:0.15mm OPTIMAL SOLUBLE FULL @MK2.5] inherits = 0.15mm OPTIMAL SOLUBLE FULL support_material_extruder = 5 support_material_interface_extruder = 5 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 and num_extruders>1 -# MK2.5 MMU2 # [print:0.15mm OPTIMAL SOLUBLE INTERFACE @MK2.5] inherits = 0.15mm OPTIMAL SOLUBLE INTERFACE support_material_extruder = 0 support_material_interface_extruder = 5 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 and num_extruders>1 -# MK2.5 # -[print:0.20mm 100mms Linear Advance @MK2.5] -inherits = 0.20mm 100mms Linear Advance -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 -single_extruder_multi_material_priming = 0 - -# MK2.5 # -[print:0.20mm NORMAL @MK2.5] -inherits = 0.20mm NORMAL -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 -single_extruder_multi_material_priming = 0 - -# MK2.5 MMU2 # [print:0.20mm NORMAL SOLUBLE FULL @MK2.5] inherits = 0.20mm NORMAL SOLUBLE FULL support_material_extruder = 5 @@ -1094,7 +776,6 @@ support_material_interface_extruder = 5 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 and num_extruders>1 single_extruder_multi_material_priming = 0 -# MK2.5 MMU2 # [print:0.20mm NORMAL SOLUBLE INTERFACE @MK2.5] inherits = 0.20mm NORMAL SOLUBLE INTERFACE support_material_extruder = 0 @@ -1102,14 +783,7 @@ support_material_interface_extruder = 5 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 and num_extruders>1 single_extruder_multi_material_priming = 0 -# MK2.5 # -[print:0.35mm FAST @MK2.5] -inherits = 0.35mm FAST -# alias = 0.35mm FAST -compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 -single_extruder_multi_material_priming = 0 - -# MK2.5 MMU2 0.6 nozzle # +# MK2.5 MMU2 0.6 nozzle [print:0.35mm SOLUBLE FULL @0.6 nozzle MK2.5] inherits = *0.35mm*; *0.6nozzle*; *soluble_support* compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and printer_model!="MK2SMM" and nozzle_diameter[0]==0.6 and num_extruders>1 @@ -1132,12 +806,296 @@ support_material_interface_layers = 3 support_material_with_sheath = 0 support_material_xy_spacing = 80% -## 0.8mm nozzle print profiles +## MK3 family ## + +## MK3 - 0.4mm nozzle + +[print:0.05mm ULTRADETAIL @MK3] +inherits = *0.05mm*; *MK3* +fill_pattern = gyroid +fill_density = 15% +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and ! single_extruder_multi_material +top_infill_extrusion_width = 0.4 + +[print:0.07mm ULTRADETAIL @MK3] +inherits = *0.07mm*; *MK3* +fill_pattern = gyroid +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and ! single_extruder_multi_material +top_infill_extrusion_width = 0.4 + +[print:0.10mm DETAIL @MK3] +inherits = *0.10mm*; *MK3* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 +external_perimeter_speed = 25 +infill_acceleration = 1000 +bridge_acceleration = 800 +infill_speed = 80 +max_print_speed = 200 +perimeter_speed = 45 +solid_infill_speed = 80 +top_infill_extrusion_width = 0.4 +top_solid_infill_speed = 40 +fill_pattern = gyroid +fill_density = 15% +perimeters = 3 + +[print:0.15mm QUALITY @MK3] +inherits = *0.15mm*; *MK3* +bridge_speed = 25 +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 +external_perimeter_speed = 25 +infill_acceleration = 1000 +infill_speed = 80 +max_print_speed = 200 +perimeter_speed = 45 +solid_infill_speed = 80 +top_solid_infill_speed = 40 +fill_pattern = gyroid +fill_density = 15% + +[print:0.15mm SPEED @MK3] +inherits = *0.15mm*; *MK3* +bridge_speed = 25 +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 +external_perimeter_speed = 35 +infill_acceleration = 1000 +infill_speed = 200 +max_print_speed = 200 +perimeter_speed = 60 +solid_infill_speed = 200 +top_solid_infill_speed = 50 + +[print:0.20mm QUALITY @MK3] +inherits = *0.20mm*; *MK3* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 +external_perimeter_speed = 25 +infill_acceleration = 1000 +infill_speed = 80 +max_print_speed = 200 +perimeter_speed = 45 +solid_infill_speed = 80 +top_solid_infill_speed = 40 +fill_pattern = gyroid +fill_density = 15% + +[print:0.20mm SPEED @MK3] +inherits = *0.20mm*; *MK3* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 +external_perimeter_speed = 35 +infill_acceleration = 1000 +infill_speed = 200 +max_print_speed = 200 +perimeter_speed = 60 +solid_infill_speed = 200 +top_solid_infill_speed = 50 + +[print:0.30mm DRAFT @MK3] +inherits = *0.30mm*; *MK3* +bottom_solid_layers = 3 +bridge_speed = 25 +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 +external_perimeter_speed = 35 +infill_acceleration = 1000 +infill_speed = 85 +max_print_speed = 200 +perimeter_speed = 50 +small_perimeter_speed = 30 +solid_infill_speed = 80 +top_solid_infill_speed = 40 +support_material_speed = 45 +external_perimeter_extrusion_width = 0.6 +extrusion_width = 0.5 +first_layer_extrusion_width = 0.42 +infill_extrusion_width = 0.5 +perimeter_extrusion_width = 0.5 +solid_infill_extrusion_width = 0.5 +top_infill_extrusion_width = 0.45 +support_material_extrusion_width = 0.38 +support_material_contact_distance = 0.2 + +## MK3 - MMU2 specific +[print:0.15mm SOLUBLE FULL @MK3] +inherits = 0.15mm SPEED @MK3; *soluble_support* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and num_extruders>1 +notes = Set your soluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder +support_material_extruder = 5 +support_material_interface_extruder = 5 +perimeter_speed = 40 +solid_infill_speed = 40 +infill_speed = 80 +top_infill_extrusion_width = 0.45 +top_solid_infill_speed = 30 +support_material_speed = 45 +bridge_flow_ratio = 0.8 +bridge_speed = 30 + +[print:0.15mm SOLUBLE INTERFACE @MK3] +inherits = 0.15mm SOLUBLE FULL @MK3 +notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder +support_material_extruder = 0 +support_material_interface_layers = 3 +support_material_with_sheath = 0 + +[print:0.20mm SOLUBLE FULL @MK3] +inherits = 0.20mm SPEED @MK3; *soluble_support* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and num_extruders>1 +notes = Set your soluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder +support_material_extruder = 5 +support_material_interface_extruder = 5 +perimeter_speed = 40 +solid_infill_speed = 40 +infill_speed = 80 +top_infill_extrusion_width = 0.45 +top_solid_infill_speed = 30 +support_material_speed = 45 +bridge_flow_ratio = 0.95 +bridge_speed = 30 + +[print:0.20mm SOLUBLE INTERFACE @MK3] +inherits = 0.20mm SOLUBLE FULL @MK3 +notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder +support_material_extruder = 0 +support_material_interface_layers = 3 +support_material_with_sheath = 0 + +## MK3 - 0.25mm nozzle + +[print:0.05mm ULTRADETAIL @0.25 nozzle MK3] +inherits = *0.05mm*; *0.25nozzle*; *MK3* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.25 and num_extruders==1 +fill_pattern = grid +fill_density = 20% +support_material_contact_distance = 0.07 + +[print:0.07mm ULTRADETAIL @0.25 nozzle MK3] +inherits = *0.07mm*; *0.25nozzle*; *MK3* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.25 and num_extruders==1 +infill_speed = 30 +solid_infill_speed = 30 +support_material_speed = 30 +top_solid_infill_speed = 20 +fill_pattern = grid +fill_density = 20% +support_material_contact_distance = 0.07 + +[print:0.10mm DETAIL @0.25 nozzle MK3] +inherits = *0.10mm*; *0.25nozzleMK3*; *MK3* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.25 +fill_pattern = grid +fill_density = 20% +support_material_contact_distance = 0.07 + +[print:0.15mm QUALITY @0.25 nozzle MK3] +inherits = *0.15mm*; *0.25nozzleMK3*; *MK3* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.25 +fill_pattern = grid +fill_density = 20% +support_material_contact_distance = 0.08 + +## MK3 - 0.6mm nozzle + +[print:0.15mm DETAIL @0.6 nozzle MK3] +inherits = *0.15mm*; *0.6nozzleMK3*; *MK306* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6 +external_perimeter_speed = 35 +infill_acceleration = 1000 +infill_speed = 70 +max_print_speed = 100 +perimeter_speed = 45 +solid_infill_speed = 70 +top_solid_infill_speed = 45 +support_material_contact_distance = 0.22 +bridge_flow_ratio = 1 + +[print:0.20mm DETAIL @0.6 nozzle MK3] +inherits = *0.20mm*; *0.6nozzleMK3*; *MK306* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6 +external_perimeter_speed = 35 +infill_acceleration = 1000 +infill_speed = 70 +max_print_speed = 100 +perimeter_speed = 45 +solid_infill_speed = 70 +top_solid_infill_speed = 45 +support_material_contact_distance = 0.22 +bridge_flow_ratio = 1 + +[print:0.30mm QUALITY @0.6 nozzle MK3] +inherits = *0.30mm*; *0.6nozzleMK3*; *MK306* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6 +external_perimeter_speed = 35 +infill_acceleration = 1000 +infill_speed = 70 +max_print_speed = 100 +perimeter_speed = 45 +solid_infill_speed = 70 +top_solid_infill_speed = 45 +support_material_contact_distance = 0.25 +bridge_flow_ratio = 1 + +[print:0.35mm SPEED @0.6 nozzle MK3] +inherits = *0.35mm*; *0.6nozzleMK3*; *MK306* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6 +external_perimeter_speed = 35 +infill_acceleration = 1000 +infill_speed = 70 +max_print_speed = 100 +perimeter_speed = 45 +solid_infill_speed = 70 +top_solid_infill_speed = 45 +external_perimeter_extrusion_width = 0.68 +perimeter_extrusion_width = 0.68 +infill_extrusion_width = 0.68 +solid_infill_extrusion_width = 0.68 +support_material_contact_distance = 0.25 +bridge_flow_ratio = 0.95 + +[print:0.40mm DRAFT @0.6 nozzle MK3] +inherits = *0.40mm*; *0.6nozzleMK3*; *MK306* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6 +external_perimeter_speed = 35 +infill_acceleration = 1000 +infill_speed = 70 +max_print_speed = 100 +perimeter_speed = 45 +solid_infill_speed = 70 +top_solid_infill_speed = 45 +external_perimeter_extrusion_width = 0.68 +perimeter_extrusion_width = 0.68 +infill_extrusion_width = 0.68 +solid_infill_extrusion_width = 0.68 +support_material_contact_distance = 0.25 +bridge_flow_ratio = 0.95 + +## MK3 - MMU2 specific + +[print:0.30mm SOLUBLE FULL @0.6 nozzle MK3] +inherits = 0.30mm QUALITY @0.6 nozzle MK3; *soluble_support* +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.6 and num_extruders>1 +notes = Set your soluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder +support_material_extruder = 5 +support_material_interface_extruder = 5 +support_material_speed = 40 +perimeter_speed = 40 +solid_infill_speed = 40 +top_infill_extrusion_width = 0.6 +support_material_extrusion_width = 0.6 +top_solid_infill_speed = 30 +support_material_xy_spacing = 80% + +[print:0.30mm SOLUBLE INTERFACE @0.6 nozzle MK3] +inherits = 0.30mm SOLUBLE FULL @0.6 nozzle MK3 +notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder +support_material_extruder = 0 +support_material_interface_layers = 3 +support_material_with_sheath = 0 + +## 0.8mm nozzle - MK2.5 and MK3 +## Only for MMU2 Single mode at the moment [print:0.30mm DETAIL @0.8 nozzle] inherits = *common*; *0.8nozzle* layer_height = 0.30 -## Only for MMU2 Single mode at the moment compatible_printers_condition = printer_model=~/(MK3|MK2.5).*/ and nozzle_diameter[0]==0.8 and num_extruders==1 perimeter_speed = 35 external_perimeter_speed = 25 @@ -1151,7 +1109,6 @@ support_material_speed = 40 [print:0.40mm QUALITY @0.8 nozzle] inherits = *common*; *0.8nozzle* layer_height = 0.4 -## Only for MMU2 Single mode at the moment compatible_printers_condition = printer_model=~/(MK3|MK2.5).*/ and nozzle_diameter[0]==0.8 and num_extruders==1 perimeter_speed = 35 external_perimeter_speed = 25 @@ -1165,7 +1122,6 @@ support_material_speed = 40 [print:0.55mm DRAFT @0.8 nozzle] inherits = *common*; *0.8nozzle* layer_height = 0.55 -## Only for MMU2 Single mode at the moment compatible_printers_condition = printer_model=~/(MK3|MK2.5).*/ and nozzle_diameter[0]==0.8 and num_extruders==1 perimeter_speed = 30 external_perimeter_speed = 25 @@ -1179,9 +1135,9 @@ top_solid_infill_speed = 30 external_perimeter_extrusion_width = 1 perimeter_extrusion_width = 1 -## MINI print profiles +## MINI ## -# 0.4mm nozzle +# MINI - 0.4mm nozzle [print:0.05mm ULTRADETAIL @MINI] inherits = *0.05mm*; *MINI* @@ -1194,6 +1150,8 @@ perimeter_extrusion_width = 0.4 external_perimeter_extrusion_width = 0.4 support_material_xy_spacing = 60% support_material_speed = 30 +support_material_extrusion_width = 0.35 +bridge_acceleration = 300 [print:0.07mm ULTRADETAIL @MINI] inherits = *0.07mm*; *MINI* @@ -1205,10 +1163,13 @@ small_perimeter_speed = 15 perimeter_extrusion_width = 0.4 external_perimeter_extrusion_width = 0.4 support_material_xy_spacing = 60% +support_material_extrusion_width = 0.35 +bridge_acceleration = 300 [print:0.10mm DETAIL @MINI] inherits = *0.10mm*; *MINI* -bridge_speed = 30 +bridge_speed = 20 +bridge_acceleration = 700 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.4 perimeter_speed = 40 external_perimeter_speed = 30 @@ -1219,12 +1180,10 @@ top_solid_infill_speed = 40 fill_pattern = gyroid fill_density = 15% perimeters = 3 -bridge_acceleration = 1000 support_material_xy_spacing = 60% [print:0.15mm QUALITY @MINI] inherits = *0.15mm*; *MINI* -bridge_speed = 30 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.4 perimeter_speed = 40 external_perimeter_speed = 30 @@ -1233,24 +1192,20 @@ solid_infill_speed = 80 top_solid_infill_speed = 40 fill_pattern = gyroid fill_density = 15% -bridge_flow_ratio = 0.85 support_material_xy_spacing = 60% [print:0.15mm SPEED @MINI] inherits = *0.15mm*; *MINI* -bridge_speed = 30 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.4 perimeter_speed = 50 external_perimeter_speed = 40 infill_speed = 140 solid_infill_speed = 140 top_solid_infill_speed = 40 -bridge_flow_ratio = 0.85 support_material_xy_spacing = 60% [print:0.20mm QUALITY @MINI] inherits = *0.20mm*; *MINI* -bridge_speed = 30 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.4 perimeter_speed = 40 external_perimeter_speed = 30 @@ -1263,7 +1218,6 @@ support_material_xy_spacing = 60% [print:0.20mm SPEED @MINI] inherits = *0.20mm*; *MINI* -bridge_speed = 30 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.4 perimeter_speed = 50 external_perimeter_speed = 40 @@ -1275,7 +1229,8 @@ support_material_xy_spacing = 60% [print:0.25mm DRAFT @MINI] inherits = *0.25mm*; *MINI* -bridge_speed = 30 +bridge_speed = 25 +bridge_flow_ratio = 0.95 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.4 external_perimeter_speed = 40 infill_speed = 110 @@ -1288,8 +1243,9 @@ infill_extrusion_width = 0.45 solid_infill_extrusion_width = 0.45 top_infill_extrusion_width = 0.4 support_material_xy_spacing = 60% +support_material_contact_distance = 0.2 -# 0.25mm nozzle +# MINI - 0.25mm nozzle [print:0.05mm ULTRADETAIL @0.25 nozzle MINI] inherits = *0.05mm*; *0.25nozzle*; *MINI* @@ -1297,6 +1253,7 @@ compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and fill_pattern = grid fill_density = 20% support_material_speed = 30 +support_material_contact_distance = 0.07 [print:0.07mm ULTRADETAIL @0.25 nozzle MINI] inherits = *0.07mm*; *0.25nozzle*; *MINI* @@ -1307,20 +1264,23 @@ support_material_speed = 30 top_solid_infill_speed = 20 fill_pattern = grid fill_density = 20% +support_material_contact_distance = 0.07 [print:0.10mm DETAIL @0.25 nozzle MINI] inherits = *0.10mm*; *0.25nozzleMINI*; *MINI* compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.25 fill_pattern = grid fill_density = 20% +support_material_contact_distance = 0.07 [print:0.15mm QUALITY @0.25 nozzle MINI] inherits = *0.15mm*; *0.25nozzleMINI*; *MINI* compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.25 fill_pattern = grid fill_density = 20% +support_material_contact_distance = 0.08 -# 0.6mm nozzle MINI +# MINI - 0.6mm nozzle [print:0.15mm DETAIL @0.6 nozzle MINI] inherits = *0.15mm*; *0.6nozzleMINI* @@ -1333,6 +1293,8 @@ solid_infill_speed = 70 top_solid_infill_speed = 45 infill_extrusion_width = 0.65 solid_infill_extrusion_width = 0.65 +support_material_contact_distance = 0.22 +bridge_flow_ratio = 1 [print:0.20mm DETAIL @0.6 nozzle MINI] inherits = *0.20mm*; *0.6nozzleMINI* @@ -1345,6 +1307,8 @@ solid_infill_speed = 70 top_solid_infill_speed = 45 infill_extrusion_width = 0.65 solid_infill_extrusion_width = 0.65 +support_material_contact_distance = 0.22 +bridge_flow_ratio = 1 [print:0.30mm QUALITY @0.6 nozzle MINI] inherits = *0.30mm*; *0.6nozzleMINI* @@ -1357,6 +1321,8 @@ solid_infill_speed = 65 top_solid_infill_speed = 45 external_perimeter_extrusion_width = 0.68 perimeter_extrusion_width = 0.68 +support_material_contact_distance = 0.25 +bridge_flow_ratio = 1 [print:0.35mm SPEED @0.6 nozzle MINI] inherits = *0.35mm*; *0.6nozzleMINI* @@ -1369,6 +1335,8 @@ solid_infill_speed = 60 top_solid_infill_speed = 45 external_perimeter_extrusion_width = 0.68 perimeter_extrusion_width = 0.68 +support_material_contact_distance = 0.25 +bridge_flow_ratio = 0.95 [print:0.40mm DRAFT @0.6 nozzle MINI] inherits = *0.40mm*; *0.6nozzleMINI* @@ -1383,8 +1351,10 @@ external_perimeter_extrusion_width = 0.68 perimeter_extrusion_width = 0.68 infill_extrusion_width = 0.68 solid_infill_extrusion_width = 0.68 +support_material_contact_distance = 0.25 +bridge_flow_ratio = 0.95 -# 0.8mm nozzle MINI +# MINI - 0.8mm nozzle [print:0.30mm DETAIL @0.8 nozzle MINI] inherits = 0.30mm DETAIL @0.8 nozzle @@ -1490,7 +1460,7 @@ max_fan_speed = 50 min_fan_speed = 30 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.08{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" temperature = 240 -filament_retract_length = 1.4 +filament_retract_length = 1 filament_retract_lift = 0.2 compatible_printers_condition = printer_model!="MK2SMM" and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) @@ -1500,14 +1470,14 @@ compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_model!="MK2S filament_max_volumetric_speed = 15 [filament:*PETMMU1*] -inherits = *PET* +; inherits = *PET* filament_retract_length = nil filament_retract_speed = nil filament_retract_lift = 0.2 compatible_printers_condition = printer_model=="MK2SMM" [filament:*PETMINI*] -inherits = *PET* +; inherits = *PET* filament_retract_length = nil filament_retract_speed = 40 filament_deretract_speed = 25 @@ -1518,7 +1488,7 @@ compatible_printers_condition = printer_model=="MINI" start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.6}0.12{elsif nozzle_diameter[0]==0.8}0.06{else}0.2{endif} ; Filament gcode" [filament:*PETMINI06*] -inherits = *PET* +; inherits = *PET* filament_retract_length = nil filament_retract_speed = 40 filament_deretract_speed = 25 @@ -1529,7 +1499,7 @@ start_filament_gcode = "M900 K0.12 ; Filament gcode" filament_max_volumetric_speed = 13 [filament:*ABSMINI*] -inherits = *ABS* +; inherits = *ABS* bed_temperature = 100 filament_retract_length = 2.7 filament_retract_speed = nil @@ -1861,8 +1831,8 @@ min_fan_speed = 20 max_fan_speed = 20 min_print_speed = 15 slowdown_below_layer_time = 15 -first_layer_temperature = 265 -temperature = 265 +first_layer_temperature = 260 +temperature = 260 filament_type = ASA [filament:Prusament ASA] @@ -2015,9 +1985,10 @@ filament_cost = 27.82 filament_density = 1.04 filament_spool_weight = 245 -[filament:Plasty Mladec ABS] +[filament:Filament PM ABS] inherits = *ABSC* -filament_vendor = Plasty Mladec +renamed_from = "Plasty Mladec ABS" +filament_vendor = Filament PM filament_cost = 27.82 filament_density = 1.08 filament_spool_weight = 230 @@ -2038,9 +2009,21 @@ filament_cost = 27.82 filament_density = 1.27 compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) -[filament:Plasty Mladec PETG] +[filament:Extrudr PETG] inherits = *PET* -filament_vendor = Plasty Mladec +filament_vendor = Extrudr +filament_cost = 35.45 +filament_density = 1.29 +temperature = 220 +bed_temperature = 70 +first_layer_temperature = 220 +first_layer_bed_temperature = 70 +slowdown_below_layer_time = 20 + +[filament:Filament PM PETG] +inherits = *PET* +renamed_from = "Plasty Mladec PETG" +filament_vendor = Filament PM filament_cost = 27.82 filament_density = 1.27 filament_spool_weight = 230 @@ -2301,9 +2284,10 @@ filament_vendor = Made for Prusa filament_cost = 27.82 filament_spool_weight = 230 -[filament:Plasty Mladec ABS @MMU2] +[filament:Filament PM ABS @MMU2] inherits = *ABS MMU2* -filament_vendor = Plasty Mladec +renamed_from = "Plasty Mladec ABS @MMU2" +filament_vendor = Filament PM filament_density = 1.08 filament_cost = 27.82 filament_spool_weight = 230 @@ -2402,9 +2386,10 @@ filament_density = 1.27 filament_spool_weight = 201 filament_type = PETG -[filament:Plasty Mladec PETG @0.6 nozzle] +[filament:Filament PM PETG @0.6 nozzle] inherits = *PET06* -filament_vendor = Plasty Mladec +renamed_from = "Plasty Mladec PETG @0.6 nozzle" +filament_vendor = Filament PM first_layer_temperature = 230 temperature = 240 filament_cost = 27.82 @@ -2458,7 +2443,7 @@ filament_unload_time = 12 filament_unloading_speed = 20 filament_unloading_speed_start = 120 filament_loading_speed_start = 19 -filament_retract_length = 1.4 +filament_retract_length = 1 filament_retract_lift = 0.2 [filament:*PET MMU2 06*] @@ -2471,9 +2456,10 @@ inherits = *PET MMU2* renamed_from = "Generic PET MMU2"; "Generic PETG MMU2" filament_vendor = Generic -[filament:Plasty Mladec PETG @MMU2] +[filament:Filament PM PETG @MMU2] inherits = *PET MMU2* -filament_vendor = Plasty Mladec +renamed_from = "Plasty Mladec PETG @MMU2" +filament_vendor = Filament PM filament_spool_weight = 230 [filament:Prusa PETG @MMU2] @@ -2510,10 +2496,11 @@ filament_cost = 36.29 filament_density = 1.27 filament_spool_weight = 201 -[filament:Plasty Mladec PETG @MMU2 0.6 nozzle] +[filament:Filament PM PETG @MMU2 0.6 nozzle] inherits = *PET MMU2 06* +renamed_from = "Plasty Mladec PETG @MMU2 0.6 nozzle" filament_type = PETG -filament_vendor = Plasty Mladec +filament_vendor = Filament PM filament_spool_weight = 230 [filament:Prusa PLA] @@ -2530,9 +2517,10 @@ filament_vendor = Fiberlogy filament_cost = 25.4 filament_density = 1.24 -[filament:Plasty Mladec PLA] +[filament:Filament PM PLA] inherits = *PLA* -filament_vendor = Plasty Mladec +renamed_from = "Plasty Mladec PLA" +filament_vendor = Filament PM filament_cost = 27.82 filament_density = 1.24 filament_spool_weight = 230 @@ -2576,6 +2564,26 @@ filament_vendor = EUMAKERS filament_cost = 25.4 filament_density = 1.24 +[filament:Extrudr PLA NX1] +inherits = *PLA* +filament_vendor = Extrudr +filament_cost = 22.76 +filament_density = 1.24 +temperature = 205 +bed_temperature = 60 +first_layer_temperature = 205 +first_layer_bed_temperature = 60 +full_fan_speed_layer = 3 +max_fan_speed = 90 +min_fan_speed = 30 +slowdown_below_layer_time = 20 + +[filament:Extrudr PLA NX2] +inherits = Extrudr PLA NX1 +filament_vendor = Extrudr +filament_cost = 23.63 +filament_density = 1.3 + [filament:Floreon3D PLA] inherits = *PLA* filament_vendor = Floreon3D @@ -2930,204 +2938,75 @@ temperature = 220 ## Filaments MMU1 [filament:ColorFabb HT @MMU1] -inherits = *PETMMU1* -filament_vendor = ColorFabb -bed_temperature = 110 -bridge_fan_speed = 30 -cooling = 1 -disable_fan_first_layers = 3 -fan_always_on = 0 -fan_below_layer_time = 10 -filament_cost = 58.66 -filament_density = 1.18 -filament_spool_weight = 236 -first_layer_bed_temperature = 105 -first_layer_temperature = 270 -max_fan_speed = 20 -min_fan_speed = 10 +inherits = ColorFabb HT; *PETMMU1* start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}45{endif}; Filament gcode" -temperature = 270 [filament:ColorFabb XT @MMU1] -inherits = *PETMMU1* -filament_vendor = ColorFabb -filament_type = PETG -filament_cost = 62.90 -filament_density = 1.27 -filament_spool_weight = 236 -first_layer_bed_temperature = 90 -first_layer_temperature = 260 -temperature = 270 +inherits = ColorFabb XT; *PETMMU1* [filament:ColorFabb XT-CF20 @MMU1] -inherits = *PETMMU1* -filament_vendor = ColorFabb -compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model=="MK2SMM" -extrusion_multiplier = 1.05 -filament_cost = 80.65 -filament_density = 1.35 -filament_spool_weight = 236 -filament_colour = #804040 -filament_max_volumetric_speed = 2 -first_layer_bed_temperature = 90 -first_layer_temperature = 260 +inherits = ColorFabb XT-CF20; *PETMMU1* start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}30{endif}; Filament gcode" -temperature = 260 [filament:ColorFabb nGen @MMU1] -inherits = *PETMMU1* -filament_vendor = ColorFabb -filament_cost = 21.2 -filament_density = 1.2 -filament_spool_weight = 236 -bridge_fan_speed = 40 -fan_always_on = 0 -fan_below_layer_time = 10 -filament_type = NGEN -first_layer_temperature = 240 -max_fan_speed = 35 -min_fan_speed = 20 +inherits = ColorFabb nGen; *PETMMU1* [filament:E3D Edge @MMU1] -inherits = *PETMMU1* -filament_vendor = E3D -filament_cost = 56.9 -filament_density = 1.26 -filament_type = EDGE +inherits = E3D Edge; *PETMMU1* [filament:Fillamentum CPE @MMU1] -inherits = *PETMMU1* -filament_vendor = Fillamentum -filament_cost = 56.45 -filament_density = 1.25 -filament_spool_weight = 230 -filament_type = CPE -first_layer_bed_temperature = 90 -first_layer_temperature = 275 -max_fan_speed = 50 -min_fan_speed = 50 -disable_fan_first_layers = 3 -full_fan_speed_layer = 5 -temperature = 275 +inherits = Fillamentum CPE; *PETMMU1* [filament:Generic PETG @MMU1] -inherits = *PETMMU1* +inherits = Generic PETG; *PETMMU1* renamed_from = "Generic PET MMU1"; "Generic PETG MMU1" -filament_vendor = Generic -filament_cost = 27.82 -filament_density = 1.27 [filament:Devil Design PETG @MMU1] -inherits = *PETMMU1* -filament_vendor = Devil Design -filament_cost = 20.99 -filament_density = 1.23 -filament_spool_weight = 250 -first_layer_temperature = 230 -first_layer_bed_temperature = 85 -temperature = 230 -bed_temperature = 90 +inherits = Devil Design PETG; *PETMMU1* -[filament:Plasty Mladec PETG @MMU1] -inherits = *PETMMU1* -filament_vendor = Plasty Mladec -filament_cost = 27.82 -filament_density = 1.27 -filament_spool_weight = 230 +[filament:Filament PM PETG @MMU1] +inherits = Filament PM PETG; *PETMMU1* +renamed_from = "Plasty Mladec PETG @MMU1" [filament:Verbatim PETG @MMU1] -inherits = *PETMMU1* -filament_vendor = Verbatim -filament_cost = 27.90 -filament_density = 1.27 -filament_spool_weight = 235 +inherits = Verbatim PETG; *PETMMU1* [filament:Fiberlogy PETG @MMU1] -inherits = *PETMMU1* -filament_vendor = Fiberlogy -filament_cost = 21.50 -filament_density = 1.27 +inherits = Fiberlogy PETG; *PETMMU1* [filament:Prusa PETG @MMU1] -inherits = *PETMMU1* +inherits = Prusa PETG; *PETMMU1* renamed_from = "Prusa PET MMU1"; "Prusa PETG MMU1" -filament_vendor = Made for Prusa -filament_cost = 27.82 -filament_density = 1.27 -filament_spool_weight = 230 [filament:Prusament PETG @MMU1] -inherits = *PETMMU1* -filament_vendor = Prusa Polymers -first_layer_temperature = 240 -temperature = 250 -filament_cost = 36.29 -filament_density = 1.27 -filament_spool_weight = 201 -filament_type = PETG +inherits = Prusament PETG; *PETMMU1* + +[filament:Extrudr PETG @MMU1] +inherits = Extrudr PETG; *PETMMU1* +filament_vendor = Extrudr [filament:Taulman T-Glase @MMU1] -inherits = *PETMMU1* -filament_vendor = Taulman -filament_cost = 40 -filament_density = 1.27 -bridge_fan_speed = 40 -cooling = 0 -fan_always_on = 0 -first_layer_bed_temperature = 90 -first_layer_temperature = 240 -max_fan_speed = 5 -min_fan_speed = 0 +inherits = Taulman T-Glase; *PETMMU1* start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}30{endif}; Filament gcode" [filament:Fiberthree F3 PA Pure Pro @MMU1] -inherits = *common* -filament_vendor = Fiberthree -filament_cost = 200.84 -filament_density = 1.2 -bed_temperature = 70 -first_layer_bed_temperature = 75 -first_layer_temperature = 270 -temperature = 270 -bridge_fan_speed = 30 -cooling = 1 -disable_fan_first_layers = 3 -fan_always_on = 1 -fan_below_layer_time = 20 -min_print_speed = 15 -slowdown_below_layer_time = 10 -filament_colour = #DEE0E6 +inherits = Fiberthree F3 PA Pure Pro filament_max_volumetric_speed = 4 -filament_soluble = 0 -filament_type = NYLON -max_fan_speed = 20 -min_fan_speed = 20 -start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.04{else}0.05{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K18{elsif nozzle_diameter[0]==0.8};{else}M900 K30{endif} ; Filament gcode LA 1.0" +filament_retract_length = nil +filament_retract_speed = nil +filament_retract_lift = nil +filament_retract_before_travel = nil +filament_wipe = nil compatible_printers_condition = printer_model=="MK2SMM" [filament:Fiberthree F3 PA-CF Pro @MMU1] -inherits = *common* -filament_vendor = Fiberthree -filament_cost = 208.1 -filament_density = 1.25 -bed_temperature = 70 -first_layer_bed_temperature = 75 -first_layer_temperature = 275 -temperature = 275 -bridge_fan_speed = 30 -cooling = 1 -disable_fan_first_layers = 3 -fan_always_on = 0 -fan_below_layer_time = 20 -min_print_speed = 15 -slowdown_below_layer_time = 10 -filament_colour = #DEE0E6 +inherits = Fiberthree F3 PA-CF Pro filament_max_volumetric_speed = 4 -filament_soluble = 0 -filament_type = NYLON -max_fan_speed = 0 -min_fan_speed = 0 -start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.04{else}0.05{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K18{elsif nozzle_diameter[0]==0.8};{else}M900 K30{endif} ; Filament gcode LA 1.0" +filament_retract_length = nil +filament_retract_speed = nil +filament_retract_lift = nil +filament_retract_before_travel = nil +filament_wipe = nil compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model=="MK2SMM" [filament:Fiberthree F3 PA-GF Pro @MMU1] @@ -3166,108 +3045,47 @@ compatible_printers_condition = printer_model=="MK2SMM" [filament:Generic PETG @MINI] inherits = Generic PETG; *PETMINI* renamed_from = "Generic PET MINI"; "Generic PETG MINI" -filament_vendor = Generic -filament_cost = 27.82 -filament_density = 1.27 compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Devil Design PETG @MINI] -inherits = Generic PETG; *PETMINI* -filament_vendor = Devil Design -filament_cost = 20.99 -filament_density = 1.23 -filament_spool_weight = 250 -first_layer_temperature = 230 -first_layer_bed_temperature = 85 -temperature = 230 -bed_temperature = 90 +inherits = Devil Design PETG; *PETMINI* compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]!=0.6 -[filament:Plasty Mladec PETG @MINI] -inherits = Generic PETG; *PETMINI* -filament_vendor = Plasty Mladec -filament_cost = 27.82 -filament_density = 1.27 -filament_spool_weight = 230 +[filament:Filament PM PETG @MINI] +inherits = Filament PM PETG; *PETMINI* +renamed_from = "Plasty Mladec PETG @MINI" compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]!=0.6 [filament:Verbatim PETG @MINI] -inherits = Generic PETG; *PETMINI* -filament_vendor = Verbatim -filament_cost = 27.90 -filament_density = 1.27 -filament_spool_weight = 235 +inherits = Verbatim PETG; *PETMINI* compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]!=0.6 [filament:Fiberlogy PETG @MINI] -inherits = Generic PETG; *PETMINI* -filament_vendor = Fiberlogy -filament_cost = 21.50 -filament_density = 1.27 +inherits = Fiberlogy PETG; *PETMINI* compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]!=0.6 [filament:Generic ABS @MINI] inherits = Generic ABS; *ABSMINI* -filament_vendor = Generic -filament_cost = 27.82 -filament_density = 1.08 -fan_always_on = 0 -cooling = 1 -min_fan_speed = 15 -max_fan_speed = 15 -disable_fan_first_layers = 4 -fan_below_layer_time = 30 -bridge_fan_speed = 25 compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]!=0.8 [filament:Fiberthree F3 PA Pure Pro @MINI] -inherits = *common* -filament_vendor = Fiberthree -filament_cost = 200.84 -filament_density = 1.2 -bed_temperature = 70 -first_layer_bed_temperature = 75 -first_layer_temperature = 270 -temperature = 270 -bridge_fan_speed = 30 -cooling = 1 -disable_fan_first_layers = 3 -fan_always_on = 1 -fan_below_layer_time = 20 -min_print_speed = 15 -slowdown_below_layer_time = 10 -filament_colour = #DEE0E6 +inherits = Fiberthree F3 PA Pure Pro filament_max_volumetric_speed = 4 -filament_soluble = 0 -filament_type = NYLON -max_fan_speed = 20 -min_fan_speed = 20 -start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.04{else}0.05{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K18{elsif nozzle_diameter[0]==0.8};{else}M900 K30{endif} ; Filament gcode LA 1.0" +filament_retract_length = nil +filament_retract_speed = nil +filament_retract_lift = nil +filament_retract_before_travel = nil +filament_wipe = nil compatible_printers_condition = printer_model=="MINI" [filament:Fiberthree F3 PA-CF Pro @MINI] -inherits = *common* -filament_vendor = Fiberthree -filament_cost = 208.1 -filament_density = 1.25 -bed_temperature = 70 -first_layer_bed_temperature = 75 -first_layer_temperature = 275 -temperature = 275 -bridge_fan_speed = 30 -cooling = 1 -disable_fan_first_layers = 3 -fan_always_on = 0 -fan_below_layer_time = 20 -min_print_speed = 15 -slowdown_below_layer_time = 10 -filament_colour = #DEE0E6 +inherits = Fiberthree F3 PA-CF Pro filament_max_volumetric_speed = 4 -filament_soluble = 0 -filament_type = NYLON -max_fan_speed = 0 -min_fan_speed = 0 -start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.04{else}0.05{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K18{elsif nozzle_diameter[0]==0.8};{else}M900 K30{endif} ; Filament gcode LA 1.0" +filament_retract_length = nil +filament_retract_speed = nil +filament_retract_lift = nil +filament_retract_before_travel = nil +filament_wipe = nil compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model=="MINI" [filament:Fiberthree F3 PA-GF Pro @MINI] @@ -3280,14 +3098,8 @@ max_fan_speed = 15 min_fan_speed = 15 [filament:Kimya ABS Carbon @MINI] -inherits = *ABSMINI* -filament_vendor = Kimya -filament_cost = 140.4 -filament_density = 1.032 -filament_colour = #804040 +inherits = Kimya ABS Carbon; *ABSMINI* filament_max_volumetric_speed = 6 -first_layer_temperature = 260 -temperature = 260 compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model=="MINI" [filament:Kimya ABS Kevlar @MINI] @@ -3296,146 +3108,57 @@ filament_vendor = Kimya filament_density = 1.037 [filament:Esun ABS @MINI] -inherits = Generic ABS; *ABSMINI* -filament_vendor = Esun -filament_cost = 27.82 -filament_density = 1.01 -filament_spool_weight = 265 -fan_always_on = 0 -cooling = 1 -min_fan_speed = 15 -max_fan_speed = 15 -disable_fan_first_layers = 4 -fan_below_layer_time = 30 -bridge_fan_speed = 25 +inherits = Esun ABS; *ABSMINI* [filament:Hatchbox ABS @MINI] -inherits = Generic ABS; *ABSMINI* -filament_vendor = Hatchbox -filament_cost = 27.82 -filament_density = 1.08 -filament_spool_weight = 245 -fan_always_on = 0 -cooling = 1 -min_fan_speed = 15 -max_fan_speed = 15 -disable_fan_first_layers = 4 -fan_below_layer_time = 30 -bridge_fan_speed = 25 +inherits = Hatchbox ABS; *ABSMINI* -[filament:Plasty Mladec ABS @MINI] -inherits = Generic ABS; *ABSMINI* -filament_vendor = Plasty Mladec -filament_cost = 27.82 -filament_density = 1.08 -filament_spool_weight = 230 -fan_always_on = 0 -cooling = 1 -min_fan_speed = 15 -max_fan_speed = 15 -disable_fan_first_layers = 4 -fan_below_layer_time = 30 -bridge_fan_speed = 25 +[filament:Filament PM ABS @MINI] +inherits = Filament PM ABS; *ABSMINI* +renamed_from = "Plasty Mladec ABS @MINI" [filament:Verbatim ABS @MINI] -inherits = Generic ABS; *ABSMINI* -filament_vendor = Verbatim -filament_cost = 25.87 -filament_density = 1.05 -filament_spool_weight = 235 -fan_always_on = 0 -cooling = 1 -min_fan_speed = 15 -max_fan_speed = 15 -disable_fan_first_layers = 4 -fan_below_layer_time = 30 -bridge_fan_speed = 25 +inherits = Verbatim ABS; *ABSMINI* [filament:Prusament PETG @MINI] inherits = Prusament PETG; *PETMINI* -filament_vendor = Prusa Polymers -first_layer_temperature = 240 -temperature = 250 -filament_density = 1.27 -filament_spool_weight = 201 -filament_cost = 36.29 compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +[filament:Extrudr PETG @MINI] +inherits = Extrudr PETG; *PETMINI* +filament_vendor = Extrudr + [filament:Kimya PETG Carbon @MINI] -inherits = *PETMINI* -filament_vendor = Kimya -extrusion_multiplier = 1.05 -filament_cost = 150.02 -filament_density = 1.317 -filament_colour = #804040 +inherits = Kimya PETG Carbon; *PETMINI* filament_max_volumetric_speed = 6 -first_layer_bed_temperature = 85 -first_layer_temperature = 240 -temperature = 240 filament_retract_length = nil filament_retract_lift = 0.3 compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model=="MINI" [filament:Prusament PETG @0.6 nozzle MINI] inherits = Prusament PETG; *PETMINI06* -first_layer_temperature = 240 -temperature = 250 -filament_density = 1.27 -filament_spool_weight = 201 -filament_cost = 36.29 [filament:Generic PETG @0.6 nozzle MINI] inherits = Generic PETG; *PETMINI06* renamed_from = "Generic PET 0.6 nozzle MINI"; "Generic PETG 0.6 nozzle MINI" -filament_cost = 27.82 -filament_density = 1.27 [filament:Devil Design PETG @0.6 nozzle MINI] -inherits = Generic PETG; *PETMINI06* -filament_vendor = Devil Design -first_layer_temperature = 230 -first_layer_bed_temperature = 85 -temperature = 230 -bed_temperature = 90 -filament_cost = 20.99 -filament_density = 1.23 -filament_spool_weight = 250 +inherits = Devil Design PETG; *PETMINI06* -[filament:Plasty Mladec PETG @0.6 nozzle MINI] -inherits = Generic PETG; *PETMINI06* -filament_vendor = Plasty Mladec -filament_cost = 27.82 -filament_density = 1.27 -filament_spool_weight = 230 +[filament:Filament PM PETG @0.6 nozzle MINI] +inherits = Filament PM PETG; *PETMINI06* +renamed_from = "Plasty Mladec PETG @0.6 nozzle MINI" [filament:Verbatim PETG @0.6 nozzle MINI] -inherits = Generic PETG; *PETMINI06* -filament_vendor = Verbatim -filament_spool_weight = 235 +inherits = Verbatim PETG; *PETMINI06* [filament:Fiberlogy PETG @0.6 nozzle MINI] -inherits = Generic PETG; *PETMINI06* -filament_vendor = Fiberlogy +inherits = Fiberlogy PETG; *PETMINI06* [filament:Prusament ASA @MINI] inherits = Prusament ASA; *ABSMINI* -first_layer_temperature = 260 first_layer_bed_temperature = 100 -temperature = 260 bed_temperature = 100 -fan_always_on = 1 -cooling = 1 -min_fan_speed = 20 -max_fan_speed = 20 -bridge_fan_speed = 30 -min_print_speed = 15 -slowdown_below_layer_time = 15 -disable_fan_first_layers = 4 -filament_type = ASA -filament_colour = #FFF2EC -filament_cost = 42.69 -filament_density = 1.07 -filament_spool_weight = 201 compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]!=0.8 [filament:Fillamentum Flexfill 98A @MINI] @@ -3541,89 +3264,43 @@ inherits = Fillamentum CPE; *PETMINI* first_layer_temperature = 265 first_layer_bed_temperature = 90 temperature = 265 -filament_type = CPE -filament_cost = 56.45 -filament_density = 1.25 -filament_spool_weight = 230 disable_fan_first_layers = 3 full_fan_speed_layer = 5 [filament:ColorFabb nGen @MINI] inherits = ColorFabb nGen; *PETMINI* -filament_cost = 52.46 -filament_density = 1.2 -filament_spool_weight = 236 [filament:E3D PC-ABS @MINI] inherits = E3D PC-ABS; *ABSMINI* -filament_density = 1.05 -filament_cost = 28.80 +filament_retract_length = nil +filament_retract_before_travel = nil +filament_wipe = nil [filament:Fillamentum ABS @MINI] inherits = Fillamentum ABS; *ABSMINI* -filament_cost = 32.4 -filament_density = 1.04 -filament_spool_weight = 230 -fan_always_on = 0 -cooling = 1 -min_fan_speed = 15 -max_fan_speed = 15 -disable_fan_first_layers = 4 -fan_below_layer_time = 30 -bridge_fan_speed = 25 [filament:Fillamentum ASA @MINI] inherits = Fillamentum ASA; *ABSMINI* -first_layer_temperature = 255 first_layer_bed_temperature = 100 -temperature = 255 bed_temperature = 100 -fan_always_on = 1 -cooling = 1 -min_fan_speed = 20 -max_fan_speed = 20 -min_print_speed = 15 -slowdown_below_layer_time = 15 -disable_fan_first_layers = 4 -filament_type = ASA -filament_colour = #FFF2EC -filament_cost = 38.7 -filament_density = 1.07 -filament_spool_weight = 230 [filament:Polymaker PC-Max @MINI] inherits = Polymaker PC-Max; *ABSMINI* -filament_type = PC filament_max_volumetric_speed = 7 bed_temperature = 100 -filament_colour = #FFF2EC first_layer_bed_temperature = 100 first_layer_temperature = 270 temperature = 270 -bridge_fan_speed = 0 -filament_cost = 77.3 -filament_density = 1.20 +filament_retract_length = nil +filament_retract_before_travel = nil +filament_wipe = nil [filament:Prusament PC Blend @MINI] -inherits = *ABSMINI* -filament_vendor = Prusa Polymers -filament_cost = 60.49 -filament_density = 1.22 -filament_spool_weight = 201 -fan_always_on = 0 +inherits = Prusament PC Blend; *ABSMINI* first_layer_temperature = 275 first_layer_bed_temperature = 100 temperature = 275 bed_temperature = 100 -cooling = 1 -min_fan_speed = 20 -max_fan_speed = 20 -bridge_fan_speed = 30 -min_print_speed = 15 -disable_fan_first_layers = 4 -fan_below_layer_time = 30 -filament_type = PC -filament_colour = #DEE0E6 filament_max_volumetric_speed = 7 filament_retract_length = nil filament_retract_speed = nil @@ -3634,117 +3311,43 @@ filament_wipe = nil compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]!=0.8 [filament:Prusa ABS @MINI] -inherits = *ABSMINI* -filament_vendor = Made for Prusa -filament_cost = 27.82 -filament_density = 1.08 -filament_spool_weight = 230 -fan_always_on = 0 -cooling = 1 -min_fan_speed = 15 -max_fan_speed = 15 -disable_fan_first_layers = 4 -fan_below_layer_time = 30 -bridge_fan_speed = 25 +inherits = Prusa ABS; *ABSMINI* compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]!=0.8 [filament:Generic HIPS @MINI] -inherits = *ABSMINI* -filament_vendor = Generic -filament_cost = 27.3 -filament_density = 1.04 -bridge_fan_speed = 50 -cooling = 1 -extrusion_multiplier = 1 -fan_always_on = 1 -fan_below_layer_time = 10 -filament_colour = #FFFFD7 -filament_soluble = 1 -filament_type = HIPS -first_layer_temperature = 230 -max_fan_speed = 20 -min_fan_speed = 20 -temperature = 230 +inherits = Generic HIPS; *ABSMINI* [filament:ColorFabb HT @MINI] -inherits = *PETMINI* -filament_vendor = ColorFabb +inherits = ColorFabb HT; *PETMINI* bed_temperature = 100 -bridge_fan_speed = 30 -cooling = 1 -disable_fan_first_layers = 3 -fan_always_on = 0 -fan_below_layer_time = 10 -filament_cost = 58.66 -filament_density = 1.18 -filament_spool_weight = 236 first_layer_bed_temperature = 100 -first_layer_temperature = 270 -max_fan_speed = 20 -min_fan_speed = 10 -temperature = 270 +min_fan_speed = 15 [filament:ColorFabb XT @MINI] -inherits = *PETMINI* -filament_vendor = ColorFabb -filament_type = PETG -filament_cost = 62.90 -filament_density = 1.27 -filament_spool_weight = 236 +inherits = ColorFabb XT; *PETMINI* first_layer_bed_temperature = 90 -first_layer_temperature = 260 -temperature = 270 [filament:ColorFabb XT-CF20 @MINI] -inherits = *PETMINI* -filament_vendor = ColorFabb +inherits = ColorFabb XT-CF20; *PETMINI* compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model=="MINI" -extrusion_multiplier = 1.05 -filament_cost = 80.65 -filament_density = 1.35 -filament_spool_weight = 236 -filament_colour = #804040 -filament_max_volumetric_speed = 2 first_layer_bed_temperature = 90 first_layer_temperature = 260 temperature = 260 [filament:Taulman T-Glase @MINI] -inherits = *PETMINI* -filament_vendor = Taulman -filament_cost = 40 -filament_density = 1.27 -bridge_fan_speed = 40 -cooling = 0 -fan_always_on = 0 -first_layer_bed_temperature = 90 -first_layer_temperature = 240 -max_fan_speed = 5 -min_fan_speed = 0 +inherits = Taulman T-Glase; *PETMINI* [filament:E3D Edge @MINI] -inherits = *PETMINI* -filament_vendor = E3D -filament_cost = 56.9 -filament_density = 1.26 -filament_type = EDGE +inherits = E3D Edge; *PETMINI* [filament:Prusa PETG @MINI] -inherits = *PETMINI* +inherits = Prusa PETG; *PETMINI* renamed_from = "Prusa PET MINI"; "Prusa PETG MINI" -filament_vendor = Made for Prusa -filament_cost = 27.82 -filament_density = 1.27 -filament_spool_weight = 230 compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Prusa PETG @0.6 nozzle MINI] -inherits = *PETMINI06* +inherits = Prusa PETG; *PETMINI06* renamed_from = "Prusa PET 0.6 nozzle MINI"; "Prusa PETG 0.6 nozzle MINI" -filament_vendor = Made for Prusa -filament_cost = 27.82 -filament_density = 1.27 -filament_spool_weight = 230 ## Filaments 0.8 nozzle @@ -5321,7 +4924,7 @@ retract_speed = 35 serial_port = serial_speed = 250000 single_extruder_multi_material = 0 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.2.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM204 S[machine_max_acceleration_extruding] T[machine_max_acceleration_retracting] ; MK2 firmware only supports the old M204 format\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.2.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM204 S[machine_max_acceleration_extruding] T[machine_max_acceleration_retracting] ; MK2 firmware only supports the old M204 format\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-2 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0 toolchange_gcode = use_firmware_retraction = 0 use_relative_e_distances = 1 @@ -5356,19 +4959,19 @@ printer_model = MK2SMM [printer:*mm-single*] inherits = *multimaterial* -end_gcode = G1 E-4 F2100.00000\nG91\nG1 Z1 F7200.000\nG90\nG1 X245 Y1\nG1 X240 E4\nG1 F4000\nG1 X190 E2.7 \nG1 F4600\nG1 X110 E2.8\nG1 F5200\nG1 X40 E3 \nG1 E-15.0000 F5000\nG1 E-50.0000 F5400\nG1 E-15.0000 F3000\nG1 E-12.0000 F2000\nG1 F1600\nG1 X0 Y1 E3.0000\nG1 X50 Y1 E-5.0000\nG1 F2000\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-5.0000\nG1 F2400\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-5.0000\nG1 F2400\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-3.0000\nG4 S0\nM107 ; turn off fan\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; Move print head up\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nG28 X0 ; home X axis\nM900 K0 ; reset LA\nM84 ; disable motors\n\n +end_gcode = G1 E-4 F2100\nG91\nG1 Z1 F7200\nG90\nG1 X245 Y1\nG1 X240 E4\nG1 F4000\nG1 X190 E2.7\nG1 F4600\nG1 X110 E2.8\nG1 F5200\nG1 X40 E3\nG1 E-15 F5000\nG1 E-50 F5400\nG1 E-15 F3000\nG1 E-12 F2000\nG1 F1600\nG1 X0 Y1 E3\nG1 X50 Y1 E-5\nG1 F2000\nG1 X0 Y1 E5\nG1 X50 Y1 E-5\nG1 F2400\nG1 X0 Y1 E5\nG1 X50 Y1 E-5\nG1 F2400\nG1 X0 Y1 E5\nG1 X50 Y1 E-3\nG4 S0\nM107 ; turn off fan\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; Move print head up\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nG28 X0 ; home X axis\nM900 K0 ; reset LA\nM84 ; disable motors\n\n printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK2\nPRINTER_HAS_BOWDEN -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.2.3 ; tell printer latest fw version\nM204 S[machine_max_acceleration_extruding] T[machine_max_acceleration_retracting] ; MK2 firmware only supports the old M204 format\n; Start G-Code sequence START\nT?\nM104 S[first_layer_temperature]\nM140 S[first_layer_bed_temperature]\nM109 S[first_layer_temperature]\nM190 S[first_layer_bed_temperature]\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG28 W\nG80\nG92 E0.0\nM203 E100\nM92 E140\nG1 Z0.250 F7200.000\nG1 X50.0 E80.0 F1000.0\nG1 X160.0 E20.0 F1000.0\nG1 Z0.200 F7200.000\nG1 X220.0 E13 F1000.0\nG1 X240.0 E0 F1000.0\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.2.3 ; tell printer latest fw version\nM204 S[machine_max_acceleration_extruding] T[machine_max_acceleration_retracting] ; MK2 firmware only supports the old M204 format\n; Start G-Code sequence START\nT?\nM104 S[first_layer_temperature]\nM140 S[first_layer_bed_temperature]\nM109 S[first_layer_temperature]\nM190 S[first_layer_bed_temperature]\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG28 W\nG80\nG92 E0\nM203 E100\nM92 E140\nG1 Z0.25 F7200\nG1 X50 E80 F1000\nG1 X160 E20 F1000\nG1 Z0.2 F7200\nG1 X220 E13 F1000\nG1 X240 E0 F1000\nG92 E0 default_print_profile = 0.15mm OPTIMAL [printer:*mm-multi*] inherits = *multimaterial* high_current_on_filament_swap = 1 -end_gcode = {if not has_wipe_tower}\n; Pull the filament into the cooling tubes.\nG1 E-4 F2100.00000\nG91\nG1 Z1 F7200.000\nG90\nG1 X245 Y1\nG1 X240 E4\nG1 F4000\nG1 X190 E2.7 \nG1 F4600\nG1 X110 E2.8\nG1 F5200\nG1 X40 E3 \nG1 E-15.0000 F5000\nG1 E-50.0000 F5400\nG1 E-15.0000 F3000\nG1 E-12.0000 F2000\nG1 F1600\nG1 X0 Y1 E3.0000\nG1 X50 Y1 E-5.0000\nG1 F2000\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-5.0000\nG1 F2400\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-5.0000\nG1 F2400\nG1 X0 Y1 E5.0000\nG1 X50 Y1 E-3.0000\nG4 S0\n{endif}\nM107 ; turn off fan\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; Move print head up\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nG28 X0 ; home X axis\nM900 K0 ; reset LA\nM84 ; disable motors +end_gcode = {if not has_wipe_tower}\n; Pull the filament into the cooling tubes.\nG1 E-4 F2100\nG91\nG1 Z1 F7200\nG90\nG1 X245 Y1\nG1 X240 E4\nG1 F4000\nG1 X190 E2.7\nG1 F4600\nG1 X110 E2.8\nG1 F5200\nG1 X40 E3\nG1 E-15 F5000\nG1 E-50 F5400\nG1 E-15 F3000\nG1 E-12 F2000\nG1 F1600\nG1 X0 Y1 E3\nG1 X50 Y1 E-5\nG1 F2000\nG1 X0 Y1 E5\nG1 X50 Y1 E-5\nG1 F2400\nG1 X0 Y1 E5\nG1 X50 Y1 E-5\nG1 F2400\nG1 X0 Y1 E5\nG1 X50 Y1 E-3\nG4 S0\n{endif}\nM107 ; turn off fan\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; Move print head up\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nG28 X0 ; home X axis\nM900 K0 ; reset LA\nM84 ; disable motors extruder_colour = #FFAA55;#E37BA0;#4ECDD3;#FB7259 nozzle_diameter = 0.4,0.4,0.4,0.4 printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK2\nPRINTER_HAS_BOWDEN -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.2.3 ; tell printer latest fw version\nM204 S[machine_max_acceleration_extruding] T[machine_max_acceleration_retracting] ; MK2 firmware only supports the old M204 format\n; Start G-Code sequence START\nT[initial_tool]\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG28 W\nG80\nG92 E0.0\nM203 E100 ; set max feedrate\nM92 E140 ; E-steps per filament milimeter\n{if not has_single_extruder_multi_material_priming}\nG1 Z0.250 F7200.000\nG1 X50.0 E80.0 F1000.0\nG1 X160.0 E20.0 F1000.0\nG1 Z0.200 F7200.000\nG1 X220.0 E13 F1000.0\nG1 X240.0 E0 F1000.0\n{endif}\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.2.3 ; tell printer latest fw version\nM204 S[machine_max_acceleration_extruding] T[machine_max_acceleration_retracting] ; MK2 firmware only supports the old M204 format\n; Start G-Code sequence START\nT[initial_tool]\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG28 W\nG80\nG92 E0\nM203 E100 ; set max feedrate\nM92 E140 ; E-steps per filament milimeter\n{if not has_single_extruder_multi_material_priming}\nG1 Z0.25 F7200\nG1 X50 E80 F1000\nG1 X160 E20 F1000\nG1 Z0.2 F7200\nG1 X220 E13 F1000\nG1 X240 E0 F1000\n{endif}\nG92 E0 default_print_profile = 0.15mm OPTIMAL # XXXXXXXXXXXXXXXXX @@ -5438,21 +5041,21 @@ inherits = Original Prusa i3 MK2S printer_model = MK2.5 remaining_times = 1 machine_max_jerk_e = 4.5 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0 [printer:Original Prusa i3 MK2.5 0.25 nozzle] inherits = Original Prusa i3 MK2S 0.25 nozzle printer_model = MK2.5 remaining_times = 1 machine_max_jerk_e = 4.5 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0 [printer:Original Prusa i3 MK2.5 0.6 nozzle] inherits = Original Prusa i3 MK2S 0.6 nozzle printer_model = MK2.5 remaining_times = 1 machine_max_jerk_e = 4.5 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0 [printer:Original Prusa i3 MK2.5 0.8 nozzle] inherits = Original Prusa i3 MK2S 0.6 nozzle @@ -5464,39 +5067,20 @@ min_layer_height = 0.2 retract_length = 1 remaining_times = 1 machine_max_jerk_e = 4.5 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0 default_print_profile = 0.40mm QUALITY @0.8 nozzle default_filament_profile = Prusament PLA @0.8 nozzle [printer:Original Prusa i3 MK2.5 MMU2 Single] -inherits = Original Prusa i3 MK2.5; *mm2* +inherits = *25mm2* printer_model = MK2.5MMU2 single_extruder_multi_material = 0 max_print_height = 200 -remaining_times = 1 -silent_mode = 0 -retract_lift_below = 199 -machine_max_acceleration_e = 10000 -machine_max_acceleration_extruding = 2000 -machine_max_acceleration_retracting = 1500 -machine_max_acceleration_x = 9000 -machine_max_acceleration_y = 9000 -machine_max_acceleration_z = 500 -machine_max_feedrate_e = 120 -machine_max_feedrate_x = 500 -machine_max_feedrate_y = 500 -machine_max_feedrate_z = 12 -machine_max_jerk_e = 4.5 -machine_max_jerk_x = 10 -machine_max_jerk_y = 10 -machine_max_jerk_z = 0.2 -machine_min_extruding_rate = 0 -machine_min_travel_rate = 0 default_print_profile = 0.15mm OPTIMAL @MK2.5 default_filament_profile = Prusament PLA printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK2.5\n -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\n; select extruder\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; load to nozzle\nTc\n; purge line\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n -end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+10, max_print_height)}{endif} F720 ; Move print head up\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM702 C\nG4 ; wait\nM104 S0 ; turn off temperature\nM900 K0 ; reset LA\nM84 ; disable motors +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\n; select extruder\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; load to nozzle\nTc\n; purge line\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.20 F1000\nG1 X5 E4 F1000\nG92 E0\n +end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+10, max_print_height)}{endif} F720 ; Move print head up\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM702 C\nG4 ; wait\nM104 S0 ; turn off temperature\nM900 K0 ; reset LA\nM84 ; disable motors [printer:Original Prusa i3 MK2.5 MMU2 Single 0.8 nozzle] inherits = Original Prusa i3 MK2.5S MMU2S Single 0.8 nozzle @@ -5511,87 +5095,43 @@ inherits = Original Prusa i3 MK2.5S MMU2S Single 0.25 nozzle printer_model = MK2.5MMU2 [printer:Original Prusa i3 MK2.5 MMU2] -inherits = Original Prusa i3 MK2.5; *mm2* +inherits = *25mm2* printer_model = MK2.5MMU2 max_print_height = 200 -remaining_times = 1 -silent_mode = 0 -retract_lift_below = 199 -machine_max_acceleration_e = 10000 -machine_max_acceleration_extruding = 2000 -machine_max_acceleration_retracting = 1500 -machine_max_acceleration_x = 9000 -machine_max_acceleration_y = 9000 -machine_max_acceleration_z = 500 -machine_max_feedrate_e = 120 -machine_max_feedrate_x = 500 -machine_max_feedrate_y = 500 -machine_max_feedrate_z = 12 -machine_max_jerk_e = 4.5 -machine_max_jerk_x = 10 -machine_max_jerk_y = 10 -machine_max_jerk_z = 0.2 -machine_min_extruding_rate = 0 -machine_min_travel_rate = 0 default_print_profile = 0.15mm OPTIMAL @MK2.5 printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK2.5\n single_extruder_multi_material = 1 -# The 5x nozzle diameter defines the number of extruders. Other extruder parameters -# (for example the retract values) are duplicaed from the first value, so they do not need -# to be defined explicitely. nozzle_diameter = 0.4,0.4,0.4,0.4,0.4 extruder_colour = #FF8000;#DB5182;#00FFFF;#FF4F4F;#9FFF9F -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\nG92 E0.0\n -end_gcode = ; Lift print head a bit\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; Move print head up\n{if has_wipe_tower}\nG1 E-15.0000 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n{endif}\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nG1 X0 Y210 F3000 ; home X axis\nM900 K0 ; reset LA\nM84 ; disable motors\n +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55 E32 F1073\nG1 X5 E32 F1800\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n{endif}\nG92 E0\n +end_gcode = ; Lift print head a bit\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; Move print head up\n{if has_wipe_tower}\nG1 E-15 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n{endif}\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nG1 X0 Y210 F3000 ; home X axis\nM900 K0 ; reset LA\nM84 ; disable motors\n [printer:Original Prusa i3 MK2.5S] inherits = Original Prusa i3 MK2.5 printer_model = MK2.5S -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 [printer:Original Prusa i3 MK2.5S 0.25 nozzle] inherits = Original Prusa i3 MK2.5 0.25 nozzle printer_model = MK2.5S -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 [printer:Original Prusa i3 MK2.5S 0.6 nozzle] inherits = Original Prusa i3 MK2.5 0.6 nozzle printer_model = MK2.5S -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0 [printer:Original Prusa i3 MK2.5S 0.8 nozzle] inherits = Original Prusa i3 MK2.5 0.8 nozzle printer_model = MK2.5S [printer:Original Prusa i3 MK2.5S MMU2S Single] -inherits = Original Prusa i3 MK2.5; *mm2s* +inherits = *25mm2s* printer_model = MK2.5SMMU2S single_extruder_multi_material = 0 max_print_height = 200 -remaining_times = 1 -silent_mode = 0 -retract_lift_below = 199 -machine_max_acceleration_e = 10000 -machine_max_acceleration_extruding = 2000 -machine_max_acceleration_retracting = 1500 -machine_max_acceleration_x = 9000 -machine_max_acceleration_y = 9000 -machine_max_acceleration_z = 500 -machine_max_feedrate_e = 120 -machine_max_feedrate_x = 500 -machine_max_feedrate_y = 500 -machine_max_feedrate_z = 12 -machine_max_jerk_e = 4.5 -machine_max_jerk_x = 10 -machine_max_jerk_y = 10 -machine_max_jerk_z = 0.2 -machine_min_extruding_rate = 0 -machine_min_travel_rate = 0 default_print_profile = 0.15mm OPTIMAL @MK2.5 default_filament_profile = Prusament PLA printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK2.5\n -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n -end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+10, max_print_height)}{endif} F720 ; Move print head up\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM702 C\nG4 ; wait\nM104 S0 ; turn off temperature\nM900 K0 ; reset LA\nM84 ; disable motors +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n +end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+10, max_print_height)}{endif} F720 ; Move print head up\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM702 C\nG4 ; wait\nM104 S0 ; turn off temperature\nM900 K0 ; reset LA\nM84 ; disable motors [printer:Original Prusa i3 MK2.5S MMU2S Single 0.8 nozzle] inherits = Original Prusa i3 MK2.5S MMU2S Single @@ -5601,7 +5141,7 @@ min_layer_height = 0.2 nozzle_diameter = 0.8 printer_variant = 0.8 retract_length = 1 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n default_print_profile = 0.40mm QUALITY @0.8 nozzle default_filament_profile = Prusament PLA @0.8 nozzle @@ -5623,41 +5163,19 @@ nozzle_diameter = 0.25 printer_variant = 0.25 retract_lift = 0.15 default_print_profile = 0.10mm DETAIL 0.25 nozzle -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F1400.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F1400\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n [printer:Original Prusa i3 MK2.5S MMU2S] -inherits = Original Prusa i3 MK2.5; *mm2s* +inherits = *25mm2s* printer_model = MK2.5SMMU2S max_print_height = 200 -remaining_times = 1 -silent_mode = 0 -retract_lift_below = 199 -machine_max_acceleration_e = 10000 -machine_max_acceleration_extruding = 2000 -machine_max_acceleration_retracting = 1500 -machine_max_acceleration_x = 9000 -machine_max_acceleration_y = 9000 -machine_max_acceleration_z = 500 -machine_max_feedrate_e = 120 -machine_max_feedrate_x = 500 -machine_max_feedrate_y = 500 -machine_max_feedrate_z = 12 -machine_max_jerk_e = 4.5 -machine_max_jerk_x = 10 -machine_max_jerk_y = 10 -machine_max_jerk_z = 0.2 -machine_min_extruding_rate = 0 -machine_min_travel_rate = 0 default_print_profile = 0.15mm OPTIMAL @MK2.5 printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK2.5\n single_extruder_multi_material = 1 -# The 5x nozzle diameter defines the number of extruders. Other extruder parameters -# (for example the retract values) are duplicaed from the first value, so they do not need -# to be defined explicitely. nozzle_diameter = 0.4,0.4,0.4,0.4,0.4 extruder_colour = #FF8000;#DB5182;#00FFFF;#FF4F4F;#9FFF9F -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E29.0 F1073.0\nG1 X5.0 E29.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\nG92 E0.0\n -end_gcode = ; Lift print head a bit\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; Move print head up\n{if has_wipe_tower}\nG1 E-15.0000 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n{endif}\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nG1 X0 Y210 F3000 ; home X axis\nM900 K0 ; reset LA\nM84 ; disable motors\n +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55 E29 F1073\nG1 X5 E29 F1800\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n{endif}\nG92 E0\n +end_gcode = ; Lift print head a bit\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; Move print head up\n{if has_wipe_tower}\nG1 E-15 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n{endif}\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nG1 X0 Y210 F3000 ; home X axis\nM900 K0 ; reset LA\nM84 ; disable motors\n [printer:Original Prusa i3 MK2.5S MMU2S 0.6 nozzle] inherits = Original Prusa i3 MK2.5S MMU2S @@ -5675,7 +5193,7 @@ min_layer_height = 0.15 printer_variant = 0.6 default_print_profile = 0.20mm NORMAL @0.6 nozzle -## For later use. 0.8mm nozzle profiles are only available for MMU2 Single mode at the moment. +## 0.8mm nozzle profiles are only available for MMU2 Single mode at the moment. ## [printer:Original Prusa i3 MK2.5S MMU2S 0.8 nozzle] ## inherits = Original Prusa i3 MK2.5S MMU2S @@ -5725,7 +5243,7 @@ remaining_times = 1 printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK3\n retract_lift_below = 209 max_print_height = 210 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0\nM221 S{if layer_height<0.075}100{else}95{endif}\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0\nM221 S{if layer_height<0.075}100{else}95{endif}\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} printer_model = MK3 default_print_profile = 0.15mm QUALITY @MK3 @@ -5736,7 +5254,7 @@ max_layer_height = 0.15 min_layer_height = 0.05 printer_variant = 0.25 retract_lift = 0.15 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E8.0 F700.0 ; intro line\nG1 X100.0 E12.5 F700.0 ; intro line\nG92 E0.0\nM221 S{if layer_height<0.075}100{else}95{endif}\n\n; Don't change E value below. Excessive value can damage the printer.\n{if print_settings_id=~/.*@0.25 nozzle MK3.*/}M907 E430 ; set extruder motor current{endif} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E8 F700 ; intro line\nG1 X100 E12.5 F700 ; intro line\nG92 E0\nM221 S{if layer_height<0.075}100{else}95{endif}\n\n; Don't change E value below. Excessive value can damage the printer.\n{if print_settings_id=~/.*@0.25 nozzle MK3.*/}M907 E430 ; set extruder motor current{endif} default_print_profile = 0.10mm DETAIL @0.25 nozzle MK3 [printer:Original Prusa i3 MK3 0.6 nozzle] @@ -5745,7 +5263,7 @@ nozzle_diameter = 0.6 max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0\nM221 S{if layer_height<0.075}100{else}95{endif} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0\nM221 S{if layer_height<0.075}100{else}95{endif} default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 [printer:Original Prusa i3 MK3 0.8 nozzle] @@ -5755,7 +5273,7 @@ max_layer_height = 0.6 min_layer_height = 0.2 printer_variant = 0.8 retract_length = 1 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0\nM221 S{if layer_height<0.075}100{else}95{endif} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0\nM221 S95 default_print_profile = 0.40mm QUALITY @0.8 nozzle default_filament_profile = Prusament PLA @0.8 nozzle @@ -5763,13 +5281,11 @@ default_filament_profile = Prusament PLA @0.8 nozzle inherits = Original Prusa i3 MK3 renamed_from = "Original Prusa i3 MK3S" printer_model = MK3S -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0\nM221 S{if layer_height<0.075}100{else}95{endif}\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} [printer:Original Prusa i3 MK3S & MK3S+ 0.25 nozzle] inherits = Original Prusa i3 MK3 0.25 nozzle renamed_from = "Original Prusa i3 MK3S 0.25 nozzle" printer_model = MK3S -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E8.0 F700.0 ; intro line\nG1 X100.0 E12.5 F700.0 ; intro line\nG92 E0.0\nM221 S{if layer_height<0.075}100{else}95{endif}\n\n; Don't change E value below. Excessive value can damage the printer.\n{if print_settings_id=~/.*@0.25 nozzle MK3.*/}M907 E430 ; set extruder motor current{endif} [printer:Original Prusa i3 MK3S & MK3S+ 0.6 nozzle] inherits = Original Prusa i3 MK3 0.6 nozzle @@ -5804,12 +5320,32 @@ printer_model = MK3SMMU2S default_print_profile = 0.15mm QUALITY @MK3 default_filament_profile = Prusament PLA @MMU2 +[printer:*25mm2*] +inherits = Original Prusa i3 MK2.5 +single_extruder_multi_material = 1 +cooling_tube_length = 10 +cooling_tube_retraction = 30 +parking_pos_retraction = 85 +retract_length_toolchange = 3 +extra_loading_move = -13 +default_filament_profile = Prusament PLA @MMU2 + +[printer:*25mm2s*] +inherits = Original Prusa i3 MK2.5S +single_extruder_multi_material = 1 +cooling_tube_length = 20 +cooling_tube_retraction = 40 +parking_pos_retraction = 85 +retract_length_toolchange = 3 +extra_loading_move = -25 +default_filament_profile = Prusament PLA @MMU2 + [printer:Original Prusa i3 MK3 MMU2 Single] inherits = *mm2* single_extruder_multi_material = 0 default_filament_profile = Prusament PLA -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} -end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+10, max_print_height)}{endif} F720 ; Move print head up\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM702 C\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nM84 ; disable motors +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} +end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+10, max_print_height)}{endif} F720 ; Move print head up\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM702 C\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nM84 ; disable motors [printer:Original Prusa i3 MK3 MMU2 Single 0.6 nozzle] inherits = Original Prusa i3 MK3 MMU2 Single @@ -5818,7 +5354,7 @@ nozzle_diameter = 0.6 max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0 default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 [printer:Original Prusa i3 MK3 MMU2 Single 0.8 nozzle] @@ -5829,7 +5365,7 @@ max_layer_height = 0.6 min_layer_height = 0.2 printer_variant = 0.8 retract_length = 1 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0 default_print_profile = 0.40mm QUALITY @0.8 nozzle default_filament_profile = Prusament PLA @0.8 nozzle @@ -5841,27 +5377,24 @@ max_layer_height = 0.15 min_layer_height = 0.05 printer_variant = 0.25 retract_lift = 0.15 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 E8.0 F1000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F1400.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0\n\n; Don't change E value below. Excessive value can damage the printer.\n{if print_settings_id=~/.*@0.25 nozzle MK3.*/}M907 E430 ; set extruder motor current{endif} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 E8 F1000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F1400\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0\n\n; Don't change E value below. Excessive value can damage the printer.\n{if print_settings_id=~/.*@0.25 nozzle MK3.*/}M907 E430 ; set extruder motor current{endif} default_print_profile = 0.10mm DETAIL @0.25 nozzle MK3 [printer:Original Prusa i3 MK3 MMU2] inherits = *mm2* -# The 5x nozzle diameter defines the number of extruders. Other extruder parameters -# (for example the retract values) are duplicaed from the first value, so they do not need -# to be defined explicitely. machine_max_acceleration_e = 8000,8000 nozzle_diameter = 0.4,0.4,0.4,0.4,0.4 extruder_colour = #FF8000;#DB5182;#00FFFF;#FF4F4F;#9FFF9F -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|SOLUBLE).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} -end_gcode = ; Lift print head a bit\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; Move print head up\n{if has_wipe_tower}\nG1 E-15.0000 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n{endif}\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|SOLUBLE|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nG1 X0 Y210 F3000 ; home X axis\nM84 ; disable motors\n +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55 E32 F1073\nG1 X5 E32 F1800\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|SOLUBLE).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} +end_gcode = ; Lift print head a bit\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; Move print head up\n{if has_wipe_tower}\nG1 E-15 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n{endif}\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|SOLUBLE|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nG1 X0 Y210 F3000 ; home X axis\nM84 ; disable motors\n [printer:Original Prusa i3 MK3S & MK3S+ MMU2S Single] inherits = *mm2s* renamed_from = "Original Prusa i3 MK3S MMU2S Single" single_extruder_multi_material = 0 default_filament_profile = Prusament PLA -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} -end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+10, max_print_height)}{endif} F720 ; Move print head up\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM702 C\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nM84 ; disable motors +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} +end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+10, max_print_height)}{endif} F720 ; Move print head up\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM702 C\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nM84 ; disable motors [printer:Original Prusa i3 MK3S & MK3S+ MMU2S Single 0.6 nozzle] inherits = Original Prusa i3 MK3S & MK3S+ MMU2S Single @@ -5871,7 +5404,7 @@ nozzle_diameter = 0.6 max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0 default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 [printer:Original Prusa i3 MK3S & MK3S+ MMU2S Single 0.8 nozzle] @@ -5882,7 +5415,7 @@ max_layer_height = 0.6 min_layer_height = 0.2 printer_variant = 0.8 retract_length = 1 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0 default_print_profile = 0.40mm QUALITY @0.8 nozzle default_filament_profile = Prusament PLA @0.8 nozzle @@ -5895,7 +5428,7 @@ max_layer_height = 0.15 min_layer_height = 0.05 printer_variant = 0.25 retract_lift = 0.15 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F1400.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0\n\n; Don't change E value below. Excessive value can damage the printer.\n{if print_settings_id=~/.*@0.25 nozzle MK3.*/}M907 E430 ; set extruder motor current{endif} +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F1400\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0\n\n; Don't change E value below. Excessive value can damage the printer.\n{if print_settings_id=~/.*@0.25 nozzle MK3.*/}M907 E430 ; set extruder motor current{endif} default_print_profile = 0.10mm DETAIL @0.25 nozzle MK3 [printer:Original Prusa i3 MK3S & MK3S+ MMU2S] @@ -5904,8 +5437,8 @@ renamed_from = "Original Prusa i3 MK3S MMU2S" machine_max_acceleration_e = 8000,8000 nozzle_diameter = 0.4,0.4,0.4,0.4,0.4 extruder_colour = #FF8000;#DB5182;#00FFFF;#FF4F4F;#9FFF9F -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E29.0 F1073.0\nG1 X5.0 E29.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|SOLUBLE).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} -end_gcode = ; Lift print head a bit\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; Move print head up\n{if has_wipe_tower}\nG1 E-15.0000 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n{endif}\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|SOLUBLE|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nG1 X0 Y210 F3000 ; home X axis\nM84 ; disable motors\n +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55 E29 F1073\nG1 X5 E29 F1800\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|SOLUBLE).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} +end_gcode = ; Lift print head a bit\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} ; Move print head up\n{if has_wipe_tower}\nG1 E-15 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n{endif}\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|SOLUBLE|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nG1 X0 Y210 F3000 ; home X axis\nM84 ; disable motors\n ## 0.6mm nozzle MMU2/S printer profiles @@ -5916,7 +5449,7 @@ nozzle_diameter = 0.6,0.6,0.6,0.6,0.6 max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E29.0 F1073.0\nG1 X5.0 E29.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55 E29 F1073\nG1 X5 E29 F1800\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0 default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 [printer:Original Prusa i3 MK3 MMU2 0.6 nozzle] @@ -5925,7 +5458,7 @@ nozzle_diameter = 0.6,0.6,0.6,0.6,0.6 max_layer_height = 0.40 min_layer_height = 0.15 printer_variant = 0.6 -start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0 +start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55 E32 F1073\nG1 X5 E32 F1800\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0 default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 ## 0.8mm nozzle MMU2/S printer profiles @@ -5938,7 +5471,7 @@ default_print_profile = 0.30mm QUALITY @0.6 nozzle MK3 ## max_layer_height = 0.6 ## min_layer_height = 0.2 ## printer_variant = 0.8 -## start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0 +## start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM115 U3.9.3 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0.0 ## default_print_profile = 0.40mm QUALITY @0.8 nozzle ## [printer:Original Prusa i3 MK3S & MK3S+ MMU2S 0.8 nozzle] @@ -5963,10 +5496,11 @@ thumbnails = 16x16,220x124 bed_shape = 0x0,180x0,180x180,0x180 default_filament_profile = "Prusament PLA" default_print_profile = 0.15mm QUALITY @MINI -gcode_flavor = marlin +gcode_flavor = marlinfirmware machine_max_acceleration_e = 5000 machine_max_acceleration_extruding = 1250 machine_max_acceleration_retracting = 1250 +machine_max_acceleration_travel = 1250 machine_max_acceleration_x = 1250 machine_max_acceleration_y = 1250 machine_max_acceleration_z = 400 @@ -5996,7 +5530,7 @@ retract_lift_below = 179 retract_layer_change = 1 silent_mode = 0 remaining_times = 1 -start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S170 ; set extruder temp for bed leveling\nM140 S[first_layer_bed_temperature] ; set bed temp\nM109 R170 ; wait for bed leveling temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nG28 ; home all without mesh bed level\nG29 ; mesh bed leveling \nM104 S[first_layer_temperature] ; set extruder temp\nG92 E0.0\nG1 Y-2.0 X179 F2400\nG1 Z3 F720\nM109 S[first_layer_temperature] ; wait for extruder temp\n\n; intro line\nG1 X170 F1000\nG1 Z0.2 F720\nG1 X110.0 E8.0 F900\nG1 X40.0 E10.0 F700\nG92 E0.0\n\nM221 S95 ; set flow +start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S170 ; set extruder temp for bed leveling\nM140 S[first_layer_bed_temperature] ; set bed temp\nM109 R170 ; wait for bed leveling temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nG28 ; home all without mesh bed level\nG29 ; mesh bed leveling \nM104 S[first_layer_temperature] ; set extruder temp\nG92 E0\nG1 Y-2 X179 F2400\nG1 Z3 F720\nM109 S[first_layer_temperature] ; wait for extruder temp\n\n; intro line\nG1 X170 F1000\nG1 Z0.2 F720\nG1 X110 E8 F900\nG1 X40 E10 F700\nG92 E0\n\nM221 S95 ; set flow end_gcode = G1 E-1 F2100 ; retract\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+2, max_print_height)}{endif} F720 ; Move print head up\nG1 X178 Y178 F4200 ; park print head\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} F720 ; Move print head further up\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM221 S100 ; reset flow\nM900 K0 ; reset LA\nM84 ; disable motors printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MINI\n extruder_colour = @@ -6012,7 +5546,7 @@ default_print_profile = 0.10mm DETAIL @0.25 nozzle MINI retract_length = 3 retract_lift = 0.15 retract_before_travel = 1 -start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S170 ; set extruder temp for bed leveling\nM140 S[first_layer_bed_temperature] ; set bed temp\nM109 R170 ; wait for bed leveling temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nG28 ; home all without mesh bed level\nG29 ; mesh bed leveling \nM104 S[first_layer_temperature] ; set extruder temp\nG92 E0.0\nG1 Y-2.0 X179 F2400\nG1 Z3 F720\nM109 S[first_layer_temperature] ; wait for extruder temp\n\n; intro line\nG1 X170 F1000\nG1 Z0.2 F720\nG1 X110.0 E8.0 F600\nG1 X40.0 E10.0 F400\nG92 E0.0\n\nM221 S95 ; set flow +start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S170 ; set extruder temp for bed leveling\nM140 S[first_layer_bed_temperature] ; set bed temp\nM109 R170 ; wait for bed leveling temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nG28 ; home all without mesh bed level\nG29 ; mesh bed leveling \nM104 S[first_layer_temperature] ; set extruder temp\nG92 E0\nG1 Y-2 X179 F2400\nG1 Z3 F720\nM109 S[first_layer_temperature] ; wait for extruder temp\n\n; intro line\nG1 X170 F1000\nG1 Z0.2 F720\nG1 X110 E8 F600\nG1 X40 E10 F400\nG92 E0\n\nM221 S95 ; set flow [printer:Original Prusa MINI & MINI+ 0.6 nozzle] inherits = Original Prusa MINI & MINI+ From 548ceb7acce6fdb576d2c59ffa8bcfab2684b19b Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 14 Apr 2021 07:38:50 +0200 Subject: [PATCH 266/285] Show info about custom supports and seam in ObjectList Slight refactoring in GLGizmosManager so it is easier to open a gizmo from the ObjectList --- resources/icons/info.png | Bin 0 -> 308 bytes src/slic3r/GUI/GUI_ObjectList.cpp | 68 +++++++++++++++--- src/slic3r/GUI/GUI_ObjectList.hpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 7 +- src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp | 8 ++- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 45 +++++++----- src/slic3r/GUI/Gizmos/GLGizmosManager.hpp | 2 + src/slic3r/GUI/ObjectDataViewModel.cpp | 72 ++++++++++++++++++- src/slic3r/GUI/ObjectDataViewModel.hpp | 18 ++++- 9 files changed, 190 insertions(+), 32 deletions(-) create mode 100644 resources/icons/info.png diff --git a/resources/icons/info.png b/resources/icons/info.png new file mode 100644 index 0000000000000000000000000000000000000000..9eeee9b3cdbac7fd819d3f4c3ee85414c5f1ed50 GIT binary patch literal 308 zcmV-40n7f0P)6|`_Rtm|7y^1~Ew=AW5=n0W(fT;mbLc<=CvuP!m`yl+rpgAzqp8NMnw zN!+2v9C;$1N30I+zs_}ZYEa-QtZ4bm;QmokMfEnO_zs(PV)Mv3c0VL!_bF(`IW#IJ zSafGP0SinrjIiz@J%^>R#;Ci-vyowo@ddeKY%{FzAieuODeleteWarningIcon(m_objects_model->GetParent(item)); m_objects_model->Delete(item); + update_info_items(obj_idx); } void ObjectList::del_settings_from_config(const wxDataViewItem& parent_item) @@ -2118,20 +2119,32 @@ void ObjectList::part_selection_changed() { if (item) { - if (m_objects_model->GetParent(item) == wxDataViewItem(nullptr)) { - obj_idx = m_objects_model->GetIdByItem(item); + const ItemType type = m_objects_model->GetItemType(item); + const wxDataViewItem parent = m_objects_model->GetParent(item); + const ItemType parent_type = m_objects_model->GetItemType(parent); + obj_idx = m_objects_model->GetObjectIdByItem(item); + + if (parent == wxDataViewItem(nullptr) + || type == itInfo) { og_name = _(L("Object manipulation")); m_config = &(*m_objects)[obj_idx]->config; update_and_show_manipulations = true; + + if (type == itInfo) { + Unselect(item); + assert(parent_type == itObject); + Select(parent); + InfoItemType info_type = m_objects_model->GetInfoItemType(item); + GLGizmosManager::EType gizmo_type = + info_type == InfoItemType::CustomSupports ? GLGizmosManager::EType::FdmSupports + : GLGizmosManager::EType::Seam; + GLGizmosManager& gizmos_mgr = wxGetApp().plater()->canvas3D()->get_gizmos_manager(); + if (gizmos_mgr.get_current_type() != gizmo_type) + gizmos_mgr.open_gizmo(gizmo_type); + } } else { - obj_idx = m_objects_model->GetObjectIdByItem(item); - - const ItemType type = m_objects_model->GetItemType(item); if (type & itSettings) { - const auto parent = m_objects_model->GetParent(item); - const ItemType parent_type = m_objects_model->GetItemType(parent); - if (parent_type & itObject) { og_name = _(L("Object Settings to modify")); m_config = &(*m_objects)[obj_idx]->config; @@ -2243,6 +2256,38 @@ wxDataViewItem ObjectList::add_settings_item(wxDataViewItem parent_item, const D return ret; } + +void ObjectList::update_info_items(size_t obj_idx) +{ + const ModelObject* model_object = (*m_objects)[obj_idx]; + wxDataViewItem item_obj = m_objects_model->GetItemById(obj_idx); + assert(item_obj.IsOk()); + + for (InfoItemType type : {InfoItemType::CustomSupports, InfoItemType::CustomSeam}) { + wxDataViewItem item = m_objects_model->GetInfoItemByType(item_obj, type); + bool shows = item.IsOk(); + bool should_show = printer_technology() == ptFFF + && std::any_of(model_object->volumes.begin(), model_object->volumes.end(), + [type](const ModelVolume* mv) { + return ! (type == InfoItemType::CustomSupports + ? mv->supported_facets.empty() + : mv->seam_facets.empty()); + }); + + if (! shows && should_show) { + m_objects_model->AddInfoChild(item_obj, type); + Expand(item_obj); + } + else if (shows && ! should_show) { + Unselect(item); + m_objects_model->Delete(item); + Select(item_obj); + } + } +} + + + void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed) { auto model_object = (*m_objects)[obj_idx]; @@ -2251,6 +2296,8 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed) model_object->config.has("extruder") ? model_object->config.extruder() : 0, get_mesh_errors_count(obj_idx) > 0); + update_info_items(obj_idx); + // add volumes to the object if (model_object->volumes.size() > 1) { for (const ModelVolume* volume : model_object->volumes) { @@ -3029,7 +3076,7 @@ void ObjectList::update_selections_on_canvas() if (sel_cnt == 1) { wxDataViewItem item = GetSelection(); - if (m_objects_model->GetItemType(item) & (itSettings | itInstanceRoot | itLayerRoot | itLayer)) + if (m_objects_model->GetItemType(item) & (itSettings | itInstanceRoot | itLayerRoot | itLayer | itInfo)) add_to_selection(m_objects_model->GetParent(item), selection, instance_idx, mode); else add_to_selection(item, selection, instance_idx, mode); @@ -3442,6 +3489,9 @@ void ObjectList::update_object_list_by_printer_technology() m_objects_model->GetChildren(wxDataViewItem(nullptr), object_items); for (auto& object_item : object_items) { + // update custom supports info + update_info_items(m_objects_model->GetObjectIdByItem(object_item)); + // Update Settings Item for object update_settings_item_and_selection(object_item, sel); diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index d70c53849d..5812e26f75 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -18,7 +18,6 @@ class wxBoxSizer; class wxBitmapComboBox; class wxMenuItem; -class ObjectDataViewModel; class MenuWithSeparators; namespace Slic3r { @@ -347,6 +346,7 @@ public: void update_and_show_object_settings_item(); void update_settings_item_and_selection(wxDataViewItem item, wxDataViewItemArray& selections); void update_object_list_by_printer_technology(); + void update_info_items(size_t obj_idx); void instances_to_separated_object(const int obj_idx, const std::set& inst_idx); void instances_to_separated_objects(const int obj_idx); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index f3f87cc33e..d870687129 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -7,6 +7,7 @@ #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/ImGuiWrapper.hpp" #include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/GUI_ObjectList.hpp" #include @@ -316,8 +317,12 @@ void GLGizmoFdmSupports::update_model_object() const updated |= mv->supported_facets.set(*m_triangle_selectors[idx].get()); } - if (updated) + if (updated) { + const ModelObjectPtrs& mos = wxGetApp().model().objects; + wxGetApp().obj_list()->update_info_items(std::find(mos.begin(), mos.end(), mo) - mos.begin()); + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp index 6c3a8ddd3a..9724767550 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp @@ -7,7 +7,7 @@ #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/ImGuiWrapper.hpp" #include "slic3r/GUI/Plater.hpp" - +#include "slic3r/GUI/GUI_ObjectList.hpp" #include @@ -222,8 +222,12 @@ void GLGizmoSeam::update_model_object() const updated |= mv->seam_facets.set(*m_triangle_selectors[idx].get()); } - if (updated) + if (updated) { + const ModelObjectPtrs& mos = wxGetApp().model().objects; + wxGetApp().obj_list()->update_info_items(std::find(mos.begin(), mos.end(), mo) - mos.begin()); + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 9dc785b3fa..2f1396d634 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -163,6 +163,17 @@ void GLGizmosManager::reset_all_states() m_hover = Undefined; } +bool GLGizmosManager::open_gizmo(EType type) +{ + int idx = int(type); + if (m_gizmos[idx]->is_selectable() && m_gizmos[idx]->is_activable()) { + activate_gizmo(m_current == idx ? Undefined : (EType)idx); + update_data(); + return true; + } + return false; +} + void GLGizmosManager::set_hover_id(int id) { if (!m_enabled || m_current == Undefined) @@ -266,24 +277,21 @@ bool GLGizmosManager::is_running() const bool GLGizmosManager::handle_shortcut(int key) { - if (!m_enabled) + if (!m_enabled || m_parent.get_selection().is_empty()) return false; - if (m_parent.get_selection().is_empty()) + auto it = std::find_if(m_gizmos.begin(), m_gizmos.end(), + [key](const std::unique_ptr& gizmo) { + int gizmo_key = gizmo->get_shortcut_key(); + return gizmo->is_selectable() + && ((gizmo_key == key - 64) || (gizmo_key == key - 96)); + }); + + if (it == m_gizmos.end()) return false; - bool handled = false; - - for (size_t idx : get_selectable_idxs()) { - int it_key = m_gizmos[idx]->get_shortcut_key(); - - if (m_gizmos[idx]->is_activable() && ((it_key == key - 64) || (it_key == key - 96))) { - activate_gizmo(m_current == idx ? Undefined : (EType)idx); - handled = true; - } - } - - return handled; + EType gizmo_type = EType(it - m_gizmos.begin()); + return open_gizmo(gizmo_type); } bool GLGizmosManager::is_dragging() const @@ -814,10 +822,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) if (!processed && !evt.HasModifiers()) { if (handle_shortcut(keyCode)) - { - update_data(); processed = true; - } } if (processed) @@ -1156,5 +1161,11 @@ bool GLGizmosManager::is_in_editing_mode(bool error_notification) const return true; } + +int GLGizmosManager::get_shortcut_key(GLGizmosManager::EType type) const +{ + return m_gizmos[type]->get_shortcut_key(); +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index 917a5830c2..c8951966e6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -171,6 +171,7 @@ public: void refresh_on_off_state(); void reset_all_states(); bool is_serializing() const { return m_serializing; } + bool open_gizmo(EType type); void set_hover_id(int id); void enable_grabber(EType type, unsigned int id, bool enable); @@ -228,6 +229,7 @@ public: void update_after_undo_redo(const UndoRedo::Snapshot& snapshot); int get_selectable_icons_cnt() const { return get_selectable_idxs().size(); } + int get_shortcut_key(GLGizmosManager::EType) const; private: void render_background(float left, float top, float right, float bottom, float border) const; diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index cf7b7b4796..395e329e8b 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -47,6 +47,19 @@ void ObjectDataViewModelNode::init_container() static constexpr char LayerRootIcon[] = "edit_layers_all"; static constexpr char LayerIcon[] = "edit_layers_some"; static constexpr char WarningIcon[] = "exclamation"; +static constexpr char InfoIcon[] = "info"; + +ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const InfoItemType info_type) : + m_parent(parent), + m_type(itInfo), + m_extruder(wxEmptyString) +{ + m_name = info_type == InfoItemType::CustomSupports + ? _L("Paint-on supports") + : _L("Paint-on seam"); + m_info_item_type = info_type; +} + ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const ItemType type) : m_parent(parent), @@ -69,6 +82,8 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent m_bmp = create_scaled_bitmap(LayerRootIcon); // FIXME: pass window ptr m_name = _(L("Layers")); } + else if (type == itInfo) + assert(false); if (type & (itInstanceRoot | itLayerRoot)) init_container(); @@ -250,6 +265,7 @@ ObjectDataViewModel::ObjectDataViewModel() m_volume_bmps = MenuFactory::get_volume_bitmaps(); m_warning_bmp = create_scaled_bitmap(WarningIcon); + m_info_bmp = create_scaled_bitmap(InfoIcon); } ObjectDataViewModel::~ObjectDataViewModel() @@ -330,13 +346,44 @@ wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent return child; } +wxDataViewItem ObjectDataViewModel::AddInfoChild(const wxDataViewItem &parent_item, InfoItemType info_type) +{ + ObjectDataViewModelNode *root = static_cast(parent_item.GetID()); + if (!root) return wxDataViewItem(0); + + const auto node = new ObjectDataViewModelNode(root, info_type); + + // The new item should be added according to its order in InfoItemType. + // Find last info item with lower index and append after it. + const auto& children = root->GetChildren(); + int idx = -1; + for (int i=0; iGetType() == itInfo && int(children[i]->GetInfoItemType()) < int(info_type) ) + idx = i; + } + + root->Insert(node, idx+1); + node->SetBitmap(m_info_bmp); + // notify control + const wxDataViewItem child((void*)node); + ItemAdded(parent_item, child); + return child; +} + wxDataViewItem ObjectDataViewModel::AddSettingsChild(const wxDataViewItem &parent_item) { ObjectDataViewModelNode *root = static_cast(parent_item.GetID()); if (!root) return wxDataViewItem(0); const auto node = new ObjectDataViewModelNode(root, itSettings); - root->Insert(node, 0); + + // In case there are some info items, append after them. + size_t i = 0; + for (i = 0; iGetChildCount(); ++i) + if (root->GetNthChild(i)->GetType() != itInfo) + break; + + root->Insert(node, i); // notify control const wxDataViewItem child((void*)node); ItemAdded(parent_item, child); @@ -1379,6 +1426,14 @@ ItemType ObjectDataViewModel::GetItemType(const wxDataViewItem &item) const return node->m_type < 0 ? itUndef : node->m_type; } +InfoItemType ObjectDataViewModel::GetInfoItemType(const wxDataViewItem &item) const +{ + if (!item.IsOk()) + return InfoItemType::Undef; + ObjectDataViewModelNode *node = static_cast(item.GetID()); + return node->m_info_item_type; +} + wxDataViewItem ObjectDataViewModel::GetItemByType(const wxDataViewItem &parent_item, ItemType type) const { if (!parent_item.IsOk()) @@ -1411,6 +1466,21 @@ wxDataViewItem ObjectDataViewModel::GetLayerRootItem(const wxDataViewItem &item) return GetItemByType(item, itLayerRoot); } +wxDataViewItem ObjectDataViewModel::GetInfoItemByType(const wxDataViewItem &parent_item, InfoItemType type) const +{ + if (! parent_item.IsOk()) + return wxDataViewItem(0); + + ObjectDataViewModelNode *node = static_cast(parent_item.GetID()); + for (size_t i = 0; i < node->GetChildCount(); i++) { + const ObjectDataViewModelNode* child_node = node->GetNthChild(i); + if (child_node->m_type == itInfo && child_node->m_info_item_type == type) + return wxDataViewItem((void*)child_node); + } + + return wxDataViewItem(0); // not found +} + bool ObjectDataViewModel::IsSettingsItem(const wxDataViewItem &item) const { if (!item.IsOk()) diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp index c23ec195bd..cf440f5dc4 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.hpp +++ b/src/slic3r/GUI/ObjectDataViewModel.hpp @@ -27,6 +27,7 @@ enum ItemType { itSettings = 16, itLayerRoot = 32, itLayer = 64, + itInfo = 128 }; enum ColumnNumber @@ -44,6 +45,13 @@ enum PrintIndicator piUnprintable , // unprintable }; +enum class InfoItemType +{ + Undef, + CustomSupports, + CustomSeam +}; + class ObjectDataViewModelNode; WX_DEFINE_ARRAY_PTR(ObjectDataViewModelNode*, MyObjectTreeModelNodePtrArray); @@ -69,6 +77,7 @@ class ObjectDataViewModelNode std::string m_action_icon_name = ""; ModelVolumeType m_volume_type; + InfoItemType m_info_item_type {InfoItemType::Undef}; public: ObjectDataViewModelNode(const wxString& name, @@ -104,6 +113,7 @@ public: const wxString& extruder = wxEmptyString ); ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const ItemType type); + ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const InfoItemType type); ~ObjectDataViewModelNode() { @@ -176,6 +186,7 @@ public: const wxBitmap& GetBitmap() const { return m_bmp; } const wxString& GetName() const { return m_name; } ItemType GetType() const { return m_type; } + InfoItemType GetInfoItemType() const { return m_info_item_type; } void SetIdx(const int& idx); int GetIdx() const { return m_idx; } ModelVolumeType GetVolumeType() { return m_volume_type; } @@ -244,6 +255,7 @@ class ObjectDataViewModel :public wxDataViewModel std::vector m_objects; std::vector m_volume_bmps; wxBitmap m_warning_bmp; + wxBitmap m_info_bmp; wxDataViewCtrl* m_ctrl { nullptr }; @@ -261,6 +273,7 @@ public: const int extruder = 0, const bool create_frst_child = true); wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item); + wxDataViewItem AddInfoChild(const wxDataViewItem &parent_item, InfoItemType info_type); wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, size_t num); wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, const std::vector& print_indicator); wxDataViewItem AddLayersRoot(const wxDataViewItem &parent_item); @@ -335,12 +348,15 @@ public: // In our case it is an item with all columns bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; } - ItemType GetItemType(const wxDataViewItem &item) const ; + ItemType GetItemType(const wxDataViewItem &item) const; + InfoItemType GetInfoItemType(const wxDataViewItem &item) const; wxDataViewItem GetItemByType( const wxDataViewItem &parent_item, ItemType type) const; wxDataViewItem GetSettingsItem(const wxDataViewItem &item) const; wxDataViewItem GetInstanceRootItem(const wxDataViewItem &item) const; wxDataViewItem GetLayerRootItem(const wxDataViewItem &item) const; + wxDataViewItem GetInfoItemByType(const wxDataViewItem &parent_item, InfoItemType type) const; + bool IsSettingsItem(const wxDataViewItem &item) const; void UpdateSettingsDigest( const wxDataViewItem &item, const std::vector& categories); From df3fb312684ec166c0b4361664bb374f65086d2d Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 19 Apr 2021 11:51:56 +0200 Subject: [PATCH 267/285] Info in ObjectList: Settings should be above the new info items, info items are selectable --- src/slic3r/GUI/GUI_ObjectList.cpp | 13 ++++++++----- src/slic3r/GUI/ObjectDataViewModel.cpp | 8 +------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index dbfc05a6d3..652fcc65b4 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1525,14 +1525,20 @@ void ObjectList::del_subobject_item(wxDataViewItem& item) if (type == itUndef) return; + wxDataViewItem parent = m_objects_model->GetParent(item); + if (type & itSettings) - del_settings_from_config(m_objects_model->GetParent(item)); + del_settings_from_config(parent); else if (type & itInstanceRoot && obj_idx != -1) del_instances_from_object(obj_idx); else if (type & itLayerRoot && obj_idx != -1) del_layers_from_object(obj_idx); else if (type & itLayer && obj_idx != -1) del_layer_from_object(obj_idx, m_objects_model->GetLayerRangeByItem(item)); + else if (type & itInfo && obj_idx != -1) { + Unselect(item); + Select(parent); + } else if (idx == -1) return; else if (!del_subobject_from_object(obj_idx, idx, type)) @@ -1540,7 +1546,7 @@ void ObjectList::del_subobject_item(wxDataViewItem& item) // If last volume item with warning was deleted, unmark object item if (type & itVolume && (*m_objects)[obj_idx]->get_mesh_errors_count() == 0) - m_objects_model->DeleteWarningIcon(m_objects_model->GetParent(item)); + m_objects_model->DeleteWarningIcon(parent); m_objects_model->Delete(item); update_info_items(obj_idx); @@ -2131,9 +2137,6 @@ void ObjectList::part_selection_changed() update_and_show_manipulations = true; if (type == itInfo) { - Unselect(item); - assert(parent_type == itObject); - Select(parent); InfoItemType info_type = m_objects_model->GetInfoItemType(item); GLGizmosManager::EType gizmo_type = info_type == InfoItemType::CustomSupports ? GLGizmosManager::EType::FdmSupports diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index 395e329e8b..541c2f8a9e 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -377,13 +377,7 @@ wxDataViewItem ObjectDataViewModel::AddSettingsChild(const wxDataViewItem &paren const auto node = new ObjectDataViewModelNode(root, itSettings); - // In case there are some info items, append after them. - size_t i = 0; - for (i = 0; iGetChildCount(); ++i) - if (root->GetNthChild(i)->GetType() != itInfo) - break; - - root->Insert(node, i); + root->Insert(node, 0); // notify control const wxDataViewItem child((void*)node); ItemAdded(parent_item, child); From dfe926ef63a6b0e6b9ddc6d27b64f7b1acd42799 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 19 Apr 2021 15:42:57 +0200 Subject: [PATCH 268/285] Info in ObjectList: Added variable layer height --- src/slic3r/GUI/GLCanvas3D.cpp | 4 +++ src/slic3r/GUI/GUI_ObjectList.cpp | 45 ++++++++++++++++++-------- src/slic3r/GUI/ObjectDataViewModel.cpp | 6 ++-- src/slic3r/GUI/ObjectDataViewModel.hpp | 3 +- src/slic3r/GUI/Plater.cpp | 6 ++++ src/slic3r/GUI/Plater.hpp | 1 + 6 files changed, 47 insertions(+), 18 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 7bbdc72b11..2149f21e7c 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -508,6 +508,7 @@ void GLCanvas3D::LayersEditing::reset_layer_height_profile(GLCanvas3D& canvas) m_layer_height_profile.clear(); m_layers_texture.valid = false; canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + wxGetApp().obj_list()->update_info_items(last_object_id); } void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas, float quality_factor) @@ -517,6 +518,7 @@ void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas const_cast(m_model_object)->layer_height_profile.set(m_layer_height_profile); m_layers_texture.valid = false; canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + wxGetApp().obj_list()->update_info_items(last_object_id); } void GLCanvas3D::LayersEditing::smooth_layer_height_profile(GLCanvas3D& canvas, const HeightProfileSmoothingParams& smoothing_params) @@ -526,6 +528,7 @@ void GLCanvas3D::LayersEditing::smooth_layer_height_profile(GLCanvas3D& canvas, const_cast(m_model_object)->layer_height_profile.set(m_layer_height_profile); m_layers_texture.valid = false; canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + wxGetApp().obj_list()->update_info_items(last_object_id); } void GLCanvas3D::LayersEditing::generate_layer_height_texture() @@ -565,6 +568,7 @@ void GLCanvas3D::LayersEditing::accept_changes(GLCanvas3D& canvas) wxGetApp().plater()->take_snapshot(_(L("Variable layer height - Manual edit"))); const_cast(m_model_object)->layer_height_profile.set(m_layer_height_profile); canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + wxGetApp().obj_list()->update_info_items(last_object_id); } } m_layer_height_profile_modified = false; diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 652fcc65b4..15c4578d82 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2138,12 +2138,15 @@ void ObjectList::part_selection_changed() if (type == itInfo) { InfoItemType info_type = m_objects_model->GetInfoItemType(item); - GLGizmosManager::EType gizmo_type = - info_type == InfoItemType::CustomSupports ? GLGizmosManager::EType::FdmSupports - : GLGizmosManager::EType::Seam; - GLGizmosManager& gizmos_mgr = wxGetApp().plater()->canvas3D()->get_gizmos_manager(); - if (gizmos_mgr.get_current_type() != gizmo_type) - gizmos_mgr.open_gizmo(gizmo_type); + if (info_type != InfoItemType::VariableLayerHeight) { + GLGizmosManager::EType gizmo_type = + info_type == InfoItemType::CustomSupports ? GLGizmosManager::EType::FdmSupports + : GLGizmosManager::EType::Seam; + GLGizmosManager& gizmos_mgr = wxGetApp().plater()->canvas3D()->get_gizmos_manager(); + if (gizmos_mgr.get_current_type() != gizmo_type) + gizmos_mgr.open_gizmo(gizmo_type); + } else + wxGetApp().plater()->toggle_layers_editing(true); } } else { @@ -2266,16 +2269,30 @@ void ObjectList::update_info_items(size_t obj_idx) wxDataViewItem item_obj = m_objects_model->GetItemById(obj_idx); assert(item_obj.IsOk()); - for (InfoItemType type : {InfoItemType::CustomSupports, InfoItemType::CustomSeam}) { + for (InfoItemType type : {InfoItemType::CustomSupports, + InfoItemType::CustomSeam, + InfoItemType::VariableLayerHeight}) { wxDataViewItem item = m_objects_model->GetInfoItemByType(item_obj, type); bool shows = item.IsOk(); - bool should_show = printer_technology() == ptFFF - && std::any_of(model_object->volumes.begin(), model_object->volumes.end(), - [type](const ModelVolume* mv) { - return ! (type == InfoItemType::CustomSupports - ? mv->supported_facets.empty() - : mv->seam_facets.empty()); - }); + bool should_show = false; + + switch (type) { + case InfoItemType::CustomSupports : + case InfoItemType::CustomSeam : + should_show = printer_technology() == ptFFF + && std::any_of(model_object->volumes.begin(), model_object->volumes.end(), + [type](const ModelVolume* mv) { + return ! (type == InfoItemType::CustomSupports + ? mv->supported_facets.empty() + : mv->seam_facets.empty()); + }); + break; + + case InfoItemType::VariableLayerHeight : + should_show = printer_technology() == ptFFF + && ! model_object->layer_height_profile.empty(); + break; + } if (! shows && should_show) { m_objects_model->AddInfoChild(item_obj, type); diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index 541c2f8a9e..49c75f9f2f 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -54,9 +54,9 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent m_type(itInfo), m_extruder(wxEmptyString) { - m_name = info_type == InfoItemType::CustomSupports - ? _L("Paint-on supports") - : _L("Paint-on seam"); + m_name = info_type == InfoItemType::CustomSupports ? _L("Paint-on supports") + : info_type == InfoItemType::CustomSeam ? _L("Paint-on seam") + : _L("Variable layer height"); m_info_item_type = info_type; } diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp index cf440f5dc4..1dd41bb107 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.hpp +++ b/src/slic3r/GUI/ObjectDataViewModel.hpp @@ -49,7 +49,8 @@ enum class InfoItemType { Undef, CustomSupports, - CustomSeam + CustomSeam, + VariableLayerHeight }; class ObjectDataViewModelNode; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 50adb89e41..5db983f43a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4967,6 +4967,12 @@ void Plater::convert_unit(ConversionType conv_type) } } +void Plater::toggle_layers_editing(bool enable) +{ + if (canvas3D()->is_layers_editing_enabled() != enable) + wxPostEvent(canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); +} + void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_upper, bool keep_lower, bool rotate_lower) { wxCHECK_RET(obj_idx < p->model.objects.size(), "obj_idx out of bounds"); diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 16590ed9b1..e99feee82f 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -191,6 +191,7 @@ public: bool is_selection_empty() const; void scale_selection_to_fit_print_volume(); void convert_unit(ConversionType conv_type); + void toggle_layers_editing(bool enable); void cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_upper = true, bool keep_lower = true, bool rotate_lower = false); From 3e09334162a7f3eb9411f87c0af16330fa0e103e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 20 Apr 2021 08:53:56 +0200 Subject: [PATCH 269/285] Removed mutable members from class Bed3D --- src/slic3r/GUI/3DBed.cpp | 83 +++++++++++++++++++++------------------- src/slic3r/GUI/3DBed.hpp | 24 ++++++------ 2 files changed, 55 insertions(+), 52 deletions(-) diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 74b58b052a..1c43e7eb04 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -113,7 +113,7 @@ void Bed3D::Axes::render() const glsafe(::glPopMatrix()); }; - m_arrow.init_from(stilized_arrow(16, DefaultTipRadius, DefaultTipLength, DefaultStemRadius, m_stem_length)); + const_cast(&m_arrow)->init_from(stilized_arrow(16, DefaultTipRadius, DefaultTipLength, DefaultStemRadius, m_stem_length)); GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader == nullptr) @@ -143,13 +143,6 @@ void Bed3D::Axes::render() const glsafe(::glDisable(GL_DEPTH_TEST)); } -Bed3D::Bed3D() - : m_type(Custom) - , m_vbo_id(0) - , m_scale_factor(1.0f) -{ -} - bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom) { auto check_texture = [](const std::string& texture) { @@ -193,7 +186,7 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c ExPolygon poly; for (const Vec2d& p : m_shape) { - poly.contour.append(Point(scale_(p(0)), scale_(p(1)))); + poly.contour.append({ scale_(p(0)), scale_(p(1)) }); } calc_triangles(poly); @@ -228,7 +221,8 @@ Point Bed3D::point_projection(const Point& point) const void Bed3D::render(GLCanvas3D& canvas, bool bottom, float scale_factor, bool show_axes, bool show_texture) const { - m_scale_factor = scale_factor; + float* factor = const_cast(&m_scale_factor); + *factor = scale_factor; if (show_axes) render_axes(); @@ -247,22 +241,24 @@ void Bed3D::render(GLCanvas3D& canvas, bool bottom, float scale_factor, void Bed3D::calc_bounding_boxes() const { - m_bounding_box = BoundingBoxf3(); + BoundingBoxf3* bounding_box = const_cast(&m_bounding_box); + *bounding_box = BoundingBoxf3(); for (const Vec2d& p : m_shape) { - m_bounding_box.merge(Vec3d(p(0), p(1), 0.0)); + bounding_box->merge({ p(0), p(1), 0.0 }); } - m_extended_bounding_box = m_bounding_box; + BoundingBoxf3* extended_bounding_box = const_cast(&m_extended_bounding_box); + *extended_bounding_box = m_bounding_box; // extend to contain axes - m_extended_bounding_box.merge(m_axes.get_origin() + m_axes.get_total_length() * Vec3d::Ones()); - m_extended_bounding_box.merge(m_extended_bounding_box.min + Vec3d(-Axes::DefaultTipRadius, -Axes::DefaultTipRadius, m_extended_bounding_box.max(2))); + extended_bounding_box->merge(m_axes.get_origin() + m_axes.get_total_length() * Vec3d::Ones()); + extended_bounding_box->merge(extended_bounding_box->min + Vec3d(-Axes::DefaultTipRadius, -Axes::DefaultTipRadius, extended_bounding_box->max(2))); // extend to contain model, if any BoundingBoxf3 model_bb = m_model.get_bounding_box(); if (model_bb.defined) { model_bb.translate(m_model_offset); - m_extended_bounding_box.merge(model_bb); + extended_bounding_box->merge(model_bb); } } @@ -339,21 +335,24 @@ void Bed3D::render_system(GLCanvas3D& canvas, bool bottom, bool show_texture) co void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const { + GLTexture* texture = const_cast(&m_texture); + GLTexture* temp_texture = const_cast(&m_temp_texture); + if (m_texture_filename.empty()) { - m_texture.reset(); + texture->reset(); render_default(bottom); return; } - if ((m_texture.get_id() == 0) || (m_texture.get_source() != m_texture_filename)) { - m_texture.reset(); + if (texture->get_id() == 0 || texture->get_source() != m_texture_filename) { + texture->reset(); if (boost::algorithm::iends_with(m_texture_filename, ".svg")) { // use higher resolution images if graphic card and opengl version allow GLint max_tex_size = OpenGLManager::get_gl_info().get_max_tex_size(); - if ((m_temp_texture.get_id() == 0) || (m_temp_texture.get_source() != m_texture_filename)) { + if (temp_texture->get_id() == 0 || temp_texture->get_source() != m_texture_filename) { // generate a temporary lower resolution texture to show while no main texture levels have been compressed - if (!m_temp_texture.load_from_svg_file(m_texture_filename, false, false, false, max_tex_size / 8)) { + if (!temp_texture->load_from_svg_file(m_texture_filename, false, false, false, max_tex_size / 8)) { render_default(bottom); return; } @@ -361,15 +360,15 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const } // starts generating the main texture, compression will run asynchronously - if (!m_texture.load_from_svg_file(m_texture_filename, true, true, true, max_tex_size)) { + if (!texture->load_from_svg_file(m_texture_filename, true, true, true, max_tex_size)) { render_default(bottom); return; } } else if (boost::algorithm::iends_with(m_texture_filename, ".png")) { // generate a temporary lower resolution texture to show while no main texture levels have been compressed - if ((m_temp_texture.get_id() == 0) || (m_temp_texture.get_source() != m_texture_filename)) { - if (!m_temp_texture.load_from_file(m_texture_filename, false, GLTexture::None, false)) { + if (temp_texture->get_id() == 0 || temp_texture->get_source() != m_texture_filename) { + if (!temp_texture->load_from_file(m_texture_filename, false, GLTexture::None, false)) { render_default(bottom); return; } @@ -377,7 +376,7 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const } // starts generating the main texture, compression will run asynchronously - if (!m_texture.load_from_file(m_texture_filename, true, GLTexture::MultiThreaded, true)) { + if (!texture->load_from_file(m_texture_filename, true, GLTexture::MultiThreaded, true)) { render_default(bottom); return; } @@ -387,13 +386,13 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const return; } } - else if (m_texture.unsent_compressed_data_available()) { + else if (texture->unsent_compressed_data_available()) { // sends to gpu the already available compressed levels of the main texture - m_texture.send_compressed_data_to_gpu(); + texture->send_compressed_data_to_gpu(); // the temporary texture is not needed anymore, reset it - if (m_temp_texture.get_id() != 0) - m_temp_texture.reset(); + if (temp_texture->get_id() != 0) + temp_texture->reset(); canvas.request_extra_frame(); @@ -406,9 +405,11 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const shader->set_uniform("transparent_background", bottom); shader->set_uniform("svg_source", boost::algorithm::iends_with(m_texture.get_source(), ".svg")); - if (m_vbo_id == 0) { - glsafe(::glGenBuffers(1, &m_vbo_id)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id)); + unsigned int* vbo_id = const_cast(&m_vbo_id); + + if (*vbo_id == 0) { + glsafe(::glGenBuffers(1, vbo_id)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, *vbo_id)); glsafe(::glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)m_triangles.get_vertices_data_size(), (const GLvoid*)m_triangles.get_vertices_data(), GL_STATIC_DRAW)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); } @@ -428,12 +429,12 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const GLint tex_coords_id = shader->get_attrib_location("v_tex_coords"); // show the temporary texture while no compressed data is available - GLuint tex_id = (GLuint)m_temp_texture.get_id(); + GLuint tex_id = (GLuint)temp_texture->get_id(); if (tex_id == 0) - tex_id = (GLuint)m_texture.get_id(); + tex_id = (GLuint)texture->get_id(); glsafe(::glBindTexture(GL_TEXTURE_2D, tex_id)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, *vbo_id)); if (position_id != -1) { glsafe(::glEnableVertexAttribArray(position_id)); @@ -471,24 +472,26 @@ void Bed3D::render_model() const if (m_model_filename.empty()) return; - if ((m_model.get_filename() != m_model_filename) && m_model.init_from_file(m_model_filename)) { + GLModel* model = const_cast(&m_model); + + if (model->get_filename() != m_model_filename && model->init_from_file(m_model_filename)) { // move the model so that its origin (0.0, 0.0, 0.0) goes into the bed shape center and a bit down to avoid z-fighting with the texture quad Vec3d shift = m_bounding_box.center(); shift(2) = -0.03; - m_model_offset = shift; + *const_cast(&m_model_offset) = shift; // update extended bounding box calc_bounding_boxes(); } - if (!m_model.get_filename().empty()) { + if (!model->get_filename().empty()) { GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader != nullptr) { shader->start_using(); shader->set_uniform("uniform_color", m_model_color); ::glPushMatrix(); ::glTranslated(m_model_offset(0), m_model_offset(1), m_model_offset(2)); - m_model.render(); + model->render(); ::glPopMatrix(); shader->stop_using(); } @@ -511,7 +514,7 @@ void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom, bool show_texture) co void Bed3D::render_default(bool bottom) const { - m_texture.reset(); + const_cast(&m_texture)->reset(); unsigned int triangles_vcount = m_triangles.get_vertices_count(); if (triangles_vcount > 0) { diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp index 6266304bee..c2630b7993 100644 --- a/src/slic3r/GUI/3DBed.hpp +++ b/src/slic3r/GUI/3DBed.hpp @@ -48,7 +48,7 @@ class Bed3D private: Vec3d m_origin{ Vec3d::Zero() }; float m_stem_length{ DefaultStemLength }; - mutable GLModel m_arrow; + GLModel m_arrow; public: const Vec3d& get_origin() const { return m_origin; } @@ -67,28 +67,28 @@ public: }; private: - EType m_type; + EType m_type{ Custom }; Pointfs m_shape; std::string m_texture_filename; std::string m_model_filename; - mutable BoundingBoxf3 m_bounding_box; - mutable BoundingBoxf3 m_extended_bounding_box; + BoundingBoxf3 m_bounding_box; + BoundingBoxf3 m_extended_bounding_box; Polygon m_polygon; GeometryBuffer m_triangles; GeometryBuffer m_gridlines; - mutable GLTexture m_texture; - mutable GLModel m_model; - mutable Vec3d m_model_offset{ Vec3d::Zero() }; - std::array m_model_color{ 0.235f, 0.235f, 0.235f, 1.0f }; + GLTexture m_texture; // temporary texture shown until the main texture has still no levels compressed - mutable GLTexture m_temp_texture; - mutable unsigned int m_vbo_id; + GLTexture m_temp_texture; + GLModel m_model; + Vec3d m_model_offset{ Vec3d::Zero() }; + std::array m_model_color{ 0.235f, 0.235f, 0.235f, 1.0f }; + unsigned int m_vbo_id{ 0 }; Axes m_axes; - mutable float m_scale_factor; + float m_scale_factor{ 1.0f }; public: - Bed3D(); + Bed3D() = default; ~Bed3D() { reset(); } EType get_type() const { return m_type; } From 441cf62ad374ee2c4a7eaec0e4a57c2ba666e9f4 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 22 Mar 2021 22:36:09 +0100 Subject: [PATCH 270/285] fix of notification states and upload progress bar notification fadeout --- src/slic3r/GUI/NotificationManager.cpp | 33 ++++++++++++++++++-------- src/slic3r/GUI/NotificationManager.hpp | 3 +++ 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index e83a0014c4..533bcb8dc6 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -169,13 +169,16 @@ void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float init imgui.set_next_window_pos(win_pos.x, win_pos.y, ImGuiCond_Always, 1.0f, 0.0f); imgui.set_next_window_size(m_window_width, m_window_height, ImGuiCond_Always); - // find if hovered - if (m_state == EState::Hovered) - m_state = EState::Shown; - + + // find if hovered FIXME: do it only in update state? + if (m_state == EState::Hovered) { + m_state = EState::Unknown; + init(); + } + if (mouse_pos.x < win_pos.x && mouse_pos.x > win_pos.x - m_window_width && mouse_pos.y > win_pos.y && mouse_pos.y < win_pos.y + m_window_height) { ImGui::SetNextWindowFocus(); - m_state = EState::Hovered; + set_hovered(); } // color change based on fading out @@ -300,8 +303,8 @@ void NotificationManager::PopNotification::init() if (m_lines_count == 3) m_multiline = true; m_notification_start = GLCanvas3D::timestamp_now(); - //if (m_state != EState::Hidden) - // m_state = EState::Shown; + if (m_state == EState::Unknown) + m_state = EState::Shown; } void NotificationManager::PopNotification::set_next_window_size(ImGuiWrapper& imgui) { @@ -579,9 +582,10 @@ bool NotificationManager::PopNotification::update_state(bool paused, const int64 // reset timers - hovered state is set in render if (m_state == EState::Hovered) { m_current_fade_opacity = 1.0f; - m_notification_start = now; + m_state = EState::Unknown; + init(); // Timers when not fading - } else if (m_state != EState::FadingOut && m_data.duration != 0 && !paused) { + } else if (m_state != EState::NotFading && m_state != EState::FadingOut && m_data.duration != 0 && !paused) { int64_t up_time = now - m_notification_start; if (up_time >= m_data.duration * 1000) { m_state = EState::FadingOut; @@ -787,6 +791,8 @@ void NotificationManager::ProgressBarNotification::init() PopNotification::init(); m_lines_count++; m_endlines.push_back(m_endlines.back()); + if(m_state == EState::Shown) + m_state = EState::NotFading; } void NotificationManager::ProgressBarNotification::count_spaces() { @@ -826,12 +832,19 @@ void NotificationManager::ProgressBarNotification::render_bar(ImGuiWrapper& imgu ImGui::GetWindowDrawList()->AddLine(lineStart, midPoint, IM_COL32((int)(orange_color.x * 255), (int)(orange_color.y * 255), (int)(orange_color.z * 255), (1.0f * 255.f)), m_line_height * 0.2f); } //------PrintHostUploadNotification---------------- +void NotificationManager::PrintHostUploadNotification::init() +{ + ProgressBarNotification::init(); + if (m_state == EState::NotFading && m_uj_state == UploadJobState::PB_COMPLETED) + m_state = EState::Shown; +} void NotificationManager::PrintHostUploadNotification::set_percentage(float percent) { m_percentage = percent; if (percent >= 1.0f) { m_uj_state = UploadJobState::PB_COMPLETED; m_has_cancel_button = false; + init(); } else if (percent < 0.0f) { error(); } else { @@ -1123,7 +1136,7 @@ void NotificationManager::push_exporting_finished_notification(const std::string void NotificationManager::push_upload_job_notification(int id, float filesize, const std::string& filename, const std::string& host, float percentage) { std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host); - NotificationData data{ NotificationType::PrintHostUpload, NotificationLevel::ProgressBarNotification, 0, text }; + NotificationData data{ NotificationType::PrintHostUpload, NotificationLevel::ProgressBarNotification, 10, text }; push_notification_data(std::make_unique(data, m_id_provider, m_evt_handler, 0, id, filesize), 0); } void NotificationManager::set_upload_job_notification_percentage(int id, const std::string& filename, const std::string& host, float percentage) diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 4f3900aeb4..4224694c4d 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -207,6 +207,7 @@ private: Unknown, // NOT initialized Hidden, Shown, // Requesting Render at some time if duration != 0 + NotFading, // Never jumps to state Fading out even if duration says so FadingOut, // Requesting Render at some time ClosePending, // Requesting Render Finished, // Requesting Render @@ -237,6 +238,7 @@ private: int64_t next_render() const { return is_finished() ? 0 : m_next_render; } EState get_state() const { return m_state; } bool is_hovered() const { return m_state == EState::Hovered; } + void set_hovered() { if (m_state != EState::Finished || m_state != EState::ClosePending || m_state != EState::Hidden || m_state != EState::Unknown) m_state = EState::Hovered; } // Call after every size change virtual void init(); @@ -401,6 +403,7 @@ private: { m_has_cancel_button = true; } + virtual void init() override; static std::string get_upload_job_text(int id, const std::string& filename, const std::string& host) { return "[" + std::to_string(id) + "] " + filename + " -> " + host; } virtual void set_percentage(float percent) override; void cancel() { m_uj_state = UploadJobState::PB_CANCELLED; m_has_cancel_button = false; } From 78e61eddf80830749de2d472b75a7dbe3043e2bd Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 23 Mar 2021 09:43:09 +0100 Subject: [PATCH 271/285] typo fix --- src/slic3r/GUI/NotificationManager.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 4224694c4d..95532c94e4 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -238,7 +238,7 @@ private: int64_t next_render() const { return is_finished() ? 0 : m_next_render; } EState get_state() const { return m_state; } bool is_hovered() const { return m_state == EState::Hovered; } - void set_hovered() { if (m_state != EState::Finished || m_state != EState::ClosePending || m_state != EState::Hidden || m_state != EState::Unknown) m_state = EState::Hovered; } + void set_hovered() { if (m_state != EState::Finished && m_state != EState::ClosePending && m_state != EState::Hidden && m_state != EState::Unknown) m_state = EState::Hovered; } // Call after every size change virtual void init(); From 9118de4e3ce0d14451eadf816117ddc333a4327c Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 23 Mar 2021 14:46:55 +0100 Subject: [PATCH 272/285] Upload notification text fix --- src/slic3r/GUI/NotificationManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 533bcb8dc6..6c5918fdef 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -859,7 +859,7 @@ void NotificationManager::PrintHostUploadNotification::render_bar(ImGuiWrapper& case Slic3r::GUI::NotificationManager::PrintHostUploadNotification::UploadJobState::PB_PROGRESS: { ProgressBarNotification::render_bar(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y); - float uploaded = m_file_size / 100 * m_percentage; + float uploaded = m_file_size * m_percentage; std::stringstream stream; stream << std::fixed << std::setprecision(2) << (int)(m_percentage * 100) << "% - " << uploaded << " of " << m_file_size << "MB uploaded"; text = stream.str(); From c140974bf40b34345191a79d85410ea62898c2b7 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Fri, 26 Mar 2021 09:18:07 +0100 Subject: [PATCH 273/285] Changed ToolpathOuside error notification from plater to slicing error notification type so it is grayed out correctly --- src/slic3r/GUI/GLCanvas3D.cpp | 40 ++++++++++++++++++-------- src/slic3r/GUI/NotificationManager.cpp | 8 ++++++ src/slic3r/GUI/NotificationManager.hpp | 1 + 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 2149f21e7c..fa3d96420b 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -6314,31 +6314,47 @@ std::vector GLCanvas3D::_parse_colors(const std::vector& col #if ENABLE_WARNING_TEXTURE_REMOVAL void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) { + enum ErrorType{ + PLATER_WARNING, + PLATER_ERROR, + SLICING_ERROR + }; std::string text; - bool error = false; + ErrorType error = ErrorType::PLATER_WARNING; switch (warning) { case EWarning::ObjectOutside: text = _u8L("An object outside the print area was detected."); break; - case EWarning::ToolpathOutside: text = _u8L("A toolpath outside the print area was detected."); error = true; break; - case EWarning::SlaSupportsOutside: text = _u8L("SLA supports outside the print area were detected."); error = true; break; + case EWarning::ToolpathOutside: text = _u8L("A toolpath outside the print area was detected."); error = ErrorType::SLICING_ERROR; break; + case EWarning::SlaSupportsOutside: text = _u8L("SLA supports outside the print area were detected."); error = ErrorType::PLATER_ERROR; break; case EWarning::SomethingNotShown: text = _u8L("Some objects are not visible."); break; case EWarning::ObjectClashed: text = _u8L("An object outside the print area was detected.\n" "Resolve the current problem to continue slicing."); - error = true; + error = ErrorType::PLATER_ERROR; break; } auto& notification_manager = *wxGetApp().plater()->get_notification_manager(); - if (state) { - if (error) - notification_manager.push_plater_error_notification(text); - else + switch (error) + { + case PLATER_WARNING: + if (state) notification_manager.push_plater_warning_notification(text); - } - else { - if (error) - notification_manager.close_plater_error_notification(text); else notification_manager.close_plater_warning_notification(text); + break; + case PLATER_ERROR: + if (state) + notification_manager.push_plater_error_notification(text); + else + notification_manager.close_plater_error_notification(text); + break; + case SLICING_ERROR: + if (state) + notification_manager.push_slicing_error_notification(text); + else + notification_manager.close_slicing_error_notification(text); + break; + default: + break; } } #else diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 6c5918fdef..144c89a1ec 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -1072,6 +1072,14 @@ void NotificationManager::close_slicing_errors_and_warnings() } } } +void NotificationManager::close_slicing_error_notification(const std::string& text) +{ + for (std::unique_ptr& notification : m_pop_notifications) { + if (notification->get_type() == NotificationType::SlicingError && notification->compare_text(_u8L("ERROR:") + "\n" + text)) { + notification->close(); + } + } +} void NotificationManager::push_slicing_complete_notification(int timestamp, bool large) { std::string hypertext; diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 95532c94e4..74ebedde2c 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -125,6 +125,7 @@ public: // void set_slicing_warning_gray(const std::string& text, bool g); // immediately stops showing slicing errors void close_slicing_errors_and_warnings(); + void close_slicing_error_notification(const std::string& text); // Release those slicing warnings, which refer to an ObjectID, which is not in the list. // living_oids is expected to be sorted. void remove_slicing_warnings_of_released_objects(const std::vector& living_oids); From fb645c63941743a8752b216bb04a3da465c8efca Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 29 Mar 2021 18:25:44 +0200 Subject: [PATCH 274/285] two line text for upload progress bar notification --- src/slic3r/GUI/NotificationManager.cpp | 56 ++++++++++++++++++-------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 144c89a1ec..980927d987 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -327,8 +327,8 @@ void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, cons if (m_multiline) { int last_end = 0; - float starting_y = m_line_height/2;//10; - float shift_y = m_line_height;// -m_line_height / 20; + float starting_y = m_line_height/2; + float shift_y = m_line_height; for (size_t i = 0; i < m_lines_count; i++) { std::string line = m_text1.substr(last_end , m_endlines[i] - last_end); if(i < m_lines_count - 1) @@ -789,8 +789,16 @@ bool NotificationManager::ExportFinishedNotification::on_text_click() void NotificationManager::ProgressBarNotification::init() { PopNotification::init(); - m_lines_count++; - m_endlines.push_back(m_endlines.back()); + //m_lines_count++; + if(m_lines_count >= 2) { + m_lines_count = 3; + m_multiline = true; + while (m_endlines.size() < 3) + m_endlines.push_back(m_endlines.back()); + } else { + m_lines_count = 2; + m_endlines.push_back(m_endlines.back()); + } if(m_state == EState::Shown) m_state = EState::NotFading; } @@ -813,20 +821,36 @@ void NotificationManager::ProgressBarNotification::count_spaces() void NotificationManager::ProgressBarNotification::render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) { // line1 - we do not print any more text than what fits on line 1. Line 2 is bar. - ImGui::SetCursorPosX(m_left_indentation); - ImGui::SetCursorPosY(win_size_y / 2 - win_size_y / 6 - m_line_height / 2); - imgui.text(m_text1.substr(0, m_endlines[0]).c_str()); - if (m_has_cancel_button) - render_cancel_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y); - render_bar(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y); + if (m_multiline) { + // two lines text, one line bar + ImGui::SetCursorPosX(m_left_indentation); + ImGui::SetCursorPosY(m_line_height / 4); + imgui.text(m_text1.substr(0, m_endlines[0]).c_str()); + ImGui::SetCursorPosX(m_left_indentation); + ImGui::SetCursorPosY(m_line_height + m_line_height / 4); + std::string line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0), m_endlines[1] - m_endlines[0] - (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0)); + imgui.text(line.c_str()); + if (m_has_cancel_button) + render_cancel_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y); + render_bar(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y); + } else { + //one line text, one line bar + ImGui::SetCursorPosX(m_left_indentation); + ImGui::SetCursorPosY(/*win_size_y / 2 - win_size_y / 6 -*/ m_line_height / 4); + imgui.text(m_text1.substr(0, m_endlines[0]).c_str()); + if (m_has_cancel_button) + render_cancel_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y); + render_bar(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y); + } + } void NotificationManager::ProgressBarNotification::render_bar(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) { ImVec4 orange_color = ImVec4(.99f, .313f, .0f, 1.0f); ImVec4 gray_color = ImVec4(.34f, .34f, .34f, 1.0f); - ImVec2 lineEnd = ImVec2(win_pos_x - m_window_width_offset, win_pos_y + win_size_y / 2 + m_line_height / 4); - ImVec2 lineStart = ImVec2(win_pos_x - win_size_x + m_left_indentation, win_pos_y + win_size_y / 2 + m_line_height / 4); + ImVec2 lineEnd = ImVec2(win_pos_x - m_window_width_offset, win_pos_y + win_size_y / 2 + (m_multiline ? m_line_height / 2 : 0)); + ImVec2 lineStart = ImVec2(win_pos_x - win_size_x + m_left_indentation, win_pos_y + win_size_y / 2 + (m_multiline ? m_line_height / 2 : 0)); ImVec2 midPoint = ImVec2(lineStart.x + (lineEnd.x - lineStart.x) * m_percentage, lineStart.y); ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, IM_COL32((int)(gray_color.x * 255), (int)(gray_color.y * 255), (int)(gray_color.z * 255), (1.0f * 255.f)), m_line_height * 0.2f); ImGui::GetWindowDrawList()->AddLine(lineStart, midPoint, IM_COL32((int)(orange_color.x * 255), (int)(orange_color.y * 255), (int)(orange_color.z * 255), (1.0f * 255.f)), m_line_height * 0.2f); @@ -864,23 +888,23 @@ void NotificationManager::PrintHostUploadNotification::render_bar(ImGuiWrapper& stream << std::fixed << std::setprecision(2) << (int)(m_percentage * 100) << "% - " << uploaded << " of " << m_file_size << "MB uploaded"; text = stream.str(); ImGui::SetCursorPosX(m_left_indentation); - ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 /*- m_line_height / 4 * 3*/); + ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - (m_multiline ? 0 : m_line_height / 4)); break; } case Slic3r::GUI::NotificationManager::PrintHostUploadNotification::UploadJobState::PB_ERROR: text = _u8L("ERROR"); ImGui::SetCursorPosX(m_left_indentation); - ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - m_line_height / 2); + ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - (m_multiline ? m_line_height / 4 : m_line_height / 2)); break; case Slic3r::GUI::NotificationManager::PrintHostUploadNotification::UploadJobState::PB_CANCELLED: text = _u8L("CANCELED"); ImGui::SetCursorPosX(m_left_indentation); - ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - m_line_height / 2); + ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - (m_multiline ? m_line_height / 4 : m_line_height / 2)); break; case Slic3r::GUI::NotificationManager::PrintHostUploadNotification::UploadJobState::PB_COMPLETED: text = _u8L("COMPLETED"); ImGui::SetCursorPosX(m_left_indentation); - ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - m_line_height / 2); + ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - (m_multiline ? m_line_height / 4 : m_line_height / 2)); break; } From d7b385f14455cde0928e7bea90a91612afb1554d Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 30 Mar 2021 21:03:59 +0200 Subject: [PATCH 275/285] compare upload notification by id and not show id in text --- src/slic3r/GUI/NotificationManager.cpp | 44 +++++++++++++++++--------- src/slic3r/GUI/NotificationManager.hpp | 6 ++-- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 980927d987..005b3c11f1 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -552,7 +552,7 @@ void NotificationManager::PopNotification::update(const NotificationData& n) m_text2 = n.text2; init(); } -bool NotificationManager::PopNotification::compare_text(const std::string& text) +bool NotificationManager::PopNotification::compare_text(const std::string& text) const { std::wstring wt1 = boost::nowide::widen(m_text1); std::wstring wt2 = boost::nowide::widen(text); @@ -1167,39 +1167,53 @@ void NotificationManager::push_exporting_finished_notification(const std::string void NotificationManager::push_upload_job_notification(int id, float filesize, const std::string& filename, const std::string& host, float percentage) { + // find if upload with same id was not already in notification + // done by compare_jon_id not compare_text thus has to be performed here + for (std::unique_ptr& notification : m_pop_notifications) { + if (notification->get_type() == NotificationType::PrintHostUpload && dynamic_cast(notification.get())->compare_job_id(id)) { + return; + } + } std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host); NotificationData data{ NotificationType::PrintHostUpload, NotificationLevel::ProgressBarNotification, 10, text }; push_notification_data(std::make_unique(data, m_id_provider, m_evt_handler, 0, id, filesize), 0); } void NotificationManager::set_upload_job_notification_percentage(int id, const std::string& filename, const std::string& host, float percentage) { - std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host); for (std::unique_ptr& notification : m_pop_notifications) { - if (notification->get_type() == NotificationType::PrintHostUpload && notification->compare_text(text)) { - dynamic_cast(notification.get())->set_percentage(percentage); - wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); + if (notification->get_type() == NotificationType::PrintHostUpload) { + PrintHostUploadNotification* phun = dynamic_cast(notification.get()); + if (phun->compare_job_id(id)) { + phun->set_percentage(percentage); + wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); + break; + } } } } void NotificationManager::upload_job_notification_show_canceled(int id, const std::string& filename, const std::string& host) { - std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host); for (std::unique_ptr& notification : m_pop_notifications) { - if (notification->get_type() == NotificationType::PrintHostUpload && notification->compare_text(text)) { - dynamic_cast(notification.get())->cancel(); - wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); - break; + if (notification->get_type() == NotificationType::PrintHostUpload) { + PrintHostUploadNotification* phun = dynamic_cast(notification.get()); + if (phun->compare_job_id(id)) { + phun->cancel(); + wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); + break; + } } } } void NotificationManager::upload_job_notification_show_error(int id, const std::string& filename, const std::string& host) { - std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host); for (std::unique_ptr& notification : m_pop_notifications) { - if (notification->get_type() == NotificationType::PrintHostUpload && notification->compare_text(text)) { - dynamic_cast(notification.get())->error(); - wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); - break; + if (notification->get_type() == NotificationType::PrintHostUpload) { + PrintHostUploadNotification* phun = dynamic_cast(notification.get()); + if(phun->compare_job_id(id)) { + phun->error(); + wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); + break; + } } } } diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 74ebedde2c..1cf7809883 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -232,7 +232,7 @@ private: const NotificationData get_data() const { return m_data; } const bool is_gray() const { return m_is_gray; } void set_gray(bool g) { m_is_gray = g; } - bool compare_text(const std::string& text); + virtual bool compare_text(const std::string& text) const; void hide(bool h) { if (is_finished()) return; m_state = h ? EState::Hidden : EState::Unknown; } // sets m_next_render with time of next mandatory rendering. Delta is time since last render. bool update_state(bool paused, const int64_t delta); @@ -405,10 +405,12 @@ private: m_has_cancel_button = true; } virtual void init() override; - static std::string get_upload_job_text(int id, const std::string& filename, const std::string& host) { return "[" + std::to_string(id) + "] " + filename + " -> " + host; } + static std::string get_upload_job_text(int id, const std::string& filename, const std::string& host) { return /*"[" + std::to_string(id) + "] " + */filename + " -> " + host; } virtual void set_percentage(float percent) override; void cancel() { m_uj_state = UploadJobState::PB_CANCELLED; m_has_cancel_button = false; } void error() { m_uj_state = UploadJobState::PB_ERROR; m_has_cancel_button = false; } + bool compare_job_id(const int other_id) const { return m_job_id == other_id; } + virtual bool compare_text(const std::string& text) const override { return false; } protected: virtual void render_bar(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, From 429675db2d52e79d8e6c29152acefb9f9e7879cf Mon Sep 17 00:00:00 2001 From: David Kocik Date: Sun, 11 Apr 2021 11:47:20 +0200 Subject: [PATCH 276/285] Error appearance of upload notification and dividing lines with lesser impotance of spaces --- src/slic3r/GUI/NotificationManager.cpp | 185 ++++++++++++++++++------- src/slic3r/GUI/NotificationManager.hpp | 27 ++-- 2 files changed, 157 insertions(+), 55 deletions(-) diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 005b3c11f1..7f61ad7f39 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -188,22 +188,8 @@ void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float init fading_pop = true; } - // background color - if (m_is_gray) { - ImVec4 backcolor(0.7f, 0.7f, 0.7f, 0.5f); - Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity); - } - else if (m_data.level == NotificationLevel::ErrorNotification) { - ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg); - backcolor.x += 0.3f; - Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity); - } - else if (m_data.level == NotificationLevel::WarningNotification) { - ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg); - backcolor.x += 0.3f; - backcolor.y += 0.15f; - Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity); - } + bool bgrnd_color_pop = push_background_color(); + // name of window indentifies window - has to be unique string if (m_id == 0) @@ -222,13 +208,34 @@ void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float init } imgui.end(); - if (m_is_gray || m_data.level == NotificationLevel::ErrorNotification || m_data.level == NotificationLevel::WarningNotification) + if (bgrnd_color_pop) ImGui::PopStyleColor(); if (fading_pop) ImGui::PopStyleColor(2); } - +bool NotificationManager::PopNotification::push_background_color() +{ + if (m_is_gray) { + ImVec4 backcolor(0.7f, 0.7f, 0.7f, 0.5f); + Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity); + return true; + } + if (m_data.level == NotificationLevel::ErrorNotification) { + ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg); + backcolor.x += 0.3f; + Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity); + return true; + } + if (m_data.level == NotificationLevel::WarningNotification) { + ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg); + backcolor.x += 0.3f; + backcolor.y += 0.15f; + Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity); + return true; + } + return false; +} void NotificationManager::PopNotification::count_spaces() { //determine line width @@ -245,34 +252,28 @@ void NotificationManager::PopNotification::count_spaces() m_window_width = m_line_height * 25; } -void NotificationManager::PopNotification::init() +void NotificationManager::PopNotification::count_lines() { - // Do not init closing notification - if (is_finished()) - return; - - std::string text = m_text1 + " " + m_hypertext; - size_t last_end = 0; - m_lines_count = 0; + std::string text = m_text1 + " " + m_hypertext; + size_t last_end = 0; + m_lines_count = 0; - count_spaces(); - - // count lines m_endlines.clear(); while (last_end < text.length() - 1) { - size_t next_hard_end = text.find_first_of('\n', last_end); - if (next_hard_end != std::string::npos && ImGui::CalcTextSize(text.substr(last_end, next_hard_end - last_end).c_str()).x < m_window_width - m_window_width_offset) { + size_t next_hard_end = text.find_first_of('\n', last_end); + if (next_hard_end != std::string::npos && ImGui::CalcTextSize(text.substr(last_end, next_hard_end - last_end).c_str()).x < m_window_width - m_window_width_offset) { //next line is ended by '/n' m_endlines.push_back(next_hard_end); last_end = next_hard_end + 1; - } else { + } + else { // find next suitable endline if (ImGui::CalcTextSize(text.substr(last_end).c_str()).x >= m_window_width - m_window_width_offset) { // more than one line till end - size_t next_space = text.find_first_of(' ', last_end); + size_t next_space = text.find_first_of(' ', last_end); if (next_space > 0) { - size_t next_space_candidate = text.find_first_of(' ', next_space + 1); + size_t next_space_candidate = text.find_first_of(' ', next_space + 1); while (next_space_candidate > 0 && ImGui::CalcTextSize(text.substr(last_end, next_space_candidate - last_end).c_str()).x < m_window_width - m_window_width_offset) { next_space = next_space_candidate; next_space_candidate = text.find_first_of(' ', next_space + 1); @@ -286,7 +287,8 @@ void NotificationManager::PopNotification::init() } m_endlines.push_back(last_end + letter_count); last_end += letter_count; - } else { + } + else { m_endlines.push_back(next_space); last_end = next_space + 1; } @@ -300,6 +302,17 @@ void NotificationManager::PopNotification::init() } m_lines_count++; } +} + +void NotificationManager::PopNotification::init() +{ + // Do not init closing notification + if (is_finished()) + return; + + count_spaces(); + count_lines(); + if (m_lines_count == 3) m_multiline = true; m_notification_start = GLCanvas3D::timestamp_now(); @@ -802,22 +815,64 @@ void NotificationManager::ProgressBarNotification::init() if(m_state == EState::Shown) m_state = EState::NotFading; } -void NotificationManager::ProgressBarNotification::count_spaces() -{ - //determine line width - m_line_height = ImGui::CalcTextSize("A").y; - m_left_indentation = m_line_height; - if (m_data.level == NotificationLevel::ErrorNotification || m_data.level == NotificationLevel::WarningNotification) { - std::string text; - text = (m_data.level == NotificationLevel::ErrorNotification ? ImGui::ErrorMarker : ImGui::WarningMarker); - float picture_width = ImGui::CalcTextSize(text.c_str()).x; - m_left_indentation = picture_width + m_line_height / 2; + +void NotificationManager::ProgressBarNotification::count_lines() +{ + std::string text = m_text1 + " " + m_hypertext; + size_t last_end = 0; + m_lines_count = 0; + + m_endlines.clear(); + while (last_end < text.length() - 1) + { + size_t next_hard_end = text.find_first_of('\n', last_end); + if (next_hard_end != std::string::npos && ImGui::CalcTextSize(text.substr(last_end, next_hard_end - last_end).c_str()).x < m_window_width - m_window_width_offset) { + //next line is ended by '/n' + m_endlines.push_back(next_hard_end); + last_end = next_hard_end + 1; + } + else { + // find next suitable endline + if (ImGui::CalcTextSize(text.substr(last_end).c_str()).x >= m_window_width - m_window_width_offset) { + // more than one line till end + size_t next_space = text.find_first_of(' ', last_end); + if (next_space > 0) { + size_t next_space_candidate = text.find_first_of(' ', next_space + 1); + while (next_space_candidate > 0 && ImGui::CalcTextSize(text.substr(last_end, next_space_candidate - last_end).c_str()).x < m_window_width - m_window_width_offset) { + next_space = next_space_candidate; + next_space_candidate = text.find_first_of(' ', next_space + 1); + } + // when one word longer than line. Or the last space is too early. + if (ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x > m_window_width - m_window_width_offset || + ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x < (m_window_width - m_window_width_offset) / 4 * 3 + ) { + float width_of_a = ImGui::CalcTextSize("a").x; + int letter_count = (int)((m_window_width - m_window_width_offset) / width_of_a); + while (last_end + letter_count < text.size() && ImGui::CalcTextSize(text.substr(last_end, letter_count).c_str()).x < m_window_width - m_window_width_offset) { + letter_count++; + } + m_endlines.push_back(last_end + letter_count); + last_end += letter_count; + } + else { + m_endlines.push_back(next_space); + last_end = next_space + 1; + } + } + } + else { + m_endlines.push_back(text.length()); + last_end = text.length(); + } + + } + m_lines_count++; } - m_window_width_offset = m_line_height * (m_has_cancel_button ? 6 : 4); - m_window_width = m_line_height * 25; } + + void NotificationManager::ProgressBarNotification::render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) { // line1 - we do not print any more text than what fits on line 1. Line 2 is bar. @@ -862,6 +917,32 @@ void NotificationManager::PrintHostUploadNotification::init() if (m_state == EState::NotFading && m_uj_state == UploadJobState::PB_COMPLETED) m_state = EState::Shown; } +void NotificationManager::PrintHostUploadNotification::count_spaces() +{ + //determine line width + m_line_height = ImGui::CalcTextSize("A").y; + + m_left_indentation = m_line_height; + if (m_uj_state == UploadJobState::PB_ERROR) { + std::string text; + text = (m_data.level == NotificationLevel::ErrorNotification ? ImGui::ErrorMarker : ImGui::WarningMarker); + float picture_width = ImGui::CalcTextSize(text.c_str()).x; + m_left_indentation = picture_width + m_line_height / 2; + } + m_window_width_offset = m_line_height * 6; //(m_has_cancel_button ? 6 : 4); + m_window_width = m_line_height * 25; +} +bool NotificationManager::PrintHostUploadNotification::push_background_color() +{ + + if (m_uj_state == UploadJobState::PB_ERROR) { + ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg); + backcolor.x += 0.3f; + Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity); + return true; + } + return false; +} void NotificationManager::PrintHostUploadNotification::set_percentage(float percent) { m_percentage = percent; @@ -911,6 +992,16 @@ void NotificationManager::PrintHostUploadNotification::render_bar(ImGuiWrapper& imgui.text(text.c_str()); } +void NotificationManager::PrintHostUploadNotification::render_left_sign(ImGuiWrapper& imgui) +{ + if (m_uj_state == UploadJobState::PB_ERROR) { + std::string text; + text = ImGui::ErrorMarker; + ImGui::SetCursorPosX(m_line_height / 3); + ImGui::SetCursorPosY(m_window_height / 2 - m_line_height); + imgui.text(text.c_str()); + } +} void NotificationManager::PrintHostUploadNotification::render_cancel_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) { ImVec2 win_size(win_size_x, win_size_y); diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 1cf7809883..17657948e6 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -240,11 +240,9 @@ private: EState get_state() const { return m_state; } bool is_hovered() const { return m_state == EState::Hovered; } void set_hovered() { if (m_state != EState::Finished && m_state != EState::ClosePending && m_state != EState::Hidden && m_state != EState::Unknown) m_state = EState::Hovered; } - + protected: // Call after every size change virtual void init(); - // Part of init() - virtual void count_spaces(); // Calculetes correct size but not se it in imgui! virtual void set_next_window_size(ImGuiWrapper& imgui); virtual void render_text(ImGuiWrapper& imgui, @@ -258,13 +256,20 @@ private: const std::string text, bool more = false); // Left sign could be error or warning sign - void render_left_sign(ImGuiWrapper& imgui); + virtual void render_left_sign(ImGuiWrapper& imgui); virtual void render_minimize_button(ImGuiWrapper& imgui, const float win_pos_x, const float win_pos_y); // Hypertext action, returns true if notification should close. // Action is stored in NotificationData::callback as std::function virtual bool on_text_click(); - protected: + + // Part of init(), counts horizontal spacing like left indentation + virtual void count_spaces(); + // Part of init(), counts end lines + virtual void count_lines(); + // returns true if PopStyleColor should be called later to pop this push + virtual bool push_background_color(); + const NotificationData m_data; // For reusing ImGUI windows. NotificationIDProvider &m_id_provider; @@ -367,7 +372,8 @@ private: virtual void set_percentage(float percent) { m_percentage = percent; } protected: virtual void init() override; - virtual void count_spaces() override; + virtual void count_lines() override; + virtual void render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) override; @@ -378,6 +384,8 @@ private: const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) {} + virtual void render_minimize_button(ImGuiWrapper& imgui, + const float win_pos_x, const float win_pos_y) override {} float m_percentage; bool m_has_cancel_button {false}; @@ -404,20 +412,23 @@ private: { m_has_cancel_button = true; } - virtual void init() override; static std::string get_upload_job_text(int id, const std::string& filename, const std::string& host) { return /*"[" + std::to_string(id) + "] " + */filename + " -> " + host; } virtual void set_percentage(float percent) override; void cancel() { m_uj_state = UploadJobState::PB_CANCELLED; m_has_cancel_button = false; } - void error() { m_uj_state = UploadJobState::PB_ERROR; m_has_cancel_button = false; } + void error() { m_uj_state = UploadJobState::PB_ERROR; m_has_cancel_button = false; init(); } bool compare_job_id(const int other_id) const { return m_job_id == other_id; } virtual bool compare_text(const std::string& text) const override { return false; } protected: + virtual void init() override; + virtual void count_spaces() override; + virtual bool push_background_color() override; virtual void render_bar(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) override; virtual void render_cancel_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) override; + virtual void render_left_sign(ImGuiWrapper& imgui) override; // Identifies job in cancel callback int m_job_id; // Size of uploaded size to be displayed in MB From 453884f908d744609915b6e269c464cae3f4c827 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 13 Apr 2021 08:39:07 +0200 Subject: [PATCH 277/285] Check of correct suffix during PrintHostSend dialog. --- src/slic3r/GUI/PrintHostDialogs.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index f69db2ea30..762450c537 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -68,8 +68,10 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_pr combo_groups->SetValue(recent_group); } - btn_sizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL)); - + auto* szr = CreateStdDialogButtonSizer(wxOK | wxCANCEL); + auto* btn_ok = szr->GetAffirmativeButton(); + btn_sizer->Add(szr); + wxString recent_path = from_u8(app_config->get("recent", CONFIG_KEY_PATH)); if (recent_path.Length() > 0 && recent_path[recent_path.Length() - 1] != '/') { recent_path += '/'; @@ -82,6 +84,20 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_pr txt_filename->SetValue(recent_path); txt_filename->SetFocus(); + wxString suffix = recent_path.substr(recent_path.find_last_of('.')); + + btn_ok->Bind(wxEVT_BUTTON, [this, suffix](wxCommandEvent&) { + wxString path = txt_filename->GetValue(); + // .gcode suffix control + if (!path.Lower().EndsWith(suffix.Lower())) + { + wxMessageDialog msg_wingow(this, wxString::Format(_L("Upload filename doesn't end with \"%s\". Do you wish to continue?"), suffix), wxString(SLIC3R_APP_NAME), wxYES | wxNO); + if (msg_wingow.ShowModal() == wxID_NO) + return; + } + EndDialog(wxID_OK); + }); + Fit(); CenterOnParent(); From b0bb1e7b1db6d2b5bca3613df3fb5c5a087ecfa5 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 20 Apr 2021 11:42:34 +0200 Subject: [PATCH 278/285] Removed mutable members from class OpenGLManager::GLInfo --- src/slic3r/GUI/OpenGLManager.cpp | 36 +++++++++++++++----------------- src/slic3r/GUI/OpenGLManager.hpp | 14 ++++++------- 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/src/slic3r/GUI/OpenGLManager.cpp b/src/slic3r/GUI/OpenGLManager.cpp index 0b24617172..91f1f1f0b6 100644 --- a/src/slic3r/GUI/OpenGLManager.cpp +++ b/src/slic3r/GUI/OpenGLManager.cpp @@ -90,22 +90,24 @@ float OpenGLManager::GLInfo::get_max_anisotropy() const void OpenGLManager::GLInfo::detect() const { - m_version = gl_get_string_safe(GL_VERSION, "N/A"); - m_glsl_version = gl_get_string_safe(GL_SHADING_LANGUAGE_VERSION, "N/A"); - m_vendor = gl_get_string_safe(GL_VENDOR, "N/A"); - m_renderer = gl_get_string_safe(GL_RENDERER, "N/A"); + *const_cast(&m_version) = gl_get_string_safe(GL_VERSION, "N/A"); + *const_cast(&m_glsl_version) = gl_get_string_safe(GL_SHADING_LANGUAGE_VERSION, "N/A"); + *const_cast(&m_vendor) = gl_get_string_safe(GL_VENDOR, "N/A"); + *const_cast(&m_renderer) = gl_get_string_safe(GL_RENDERER, "N/A"); - glsafe(::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_max_tex_size)); + int* max_tex_size = const_cast(&m_max_tex_size); + glsafe(::glGetIntegerv(GL_MAX_TEXTURE_SIZE, max_tex_size)); - m_max_tex_size /= 2; + *max_tex_size /= 2; if (Slic3r::total_physical_memory() / (1024 * 1024 * 1024) < 6) - m_max_tex_size /= 2; + *max_tex_size /= 2; - if (GLEW_EXT_texture_filter_anisotropic) - glsafe(::glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &m_max_anisotropy)); - - m_detected = true; + if (GLEW_EXT_texture_filter_anisotropic) { + float* max_anisotropy = const_cast(&m_max_anisotropy); + glsafe(::glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy)); + } + *const_cast(&m_detected) = true; } static bool version_greater_or_equal_to(const std::string& version, unsigned int major, unsigned int minor) @@ -174,19 +176,16 @@ std::string OpenGLManager::GLInfo::to_string(bool format_as_html, bool extension out << b_start << "Renderer: " << b_end << m_renderer << line_end; out << b_start << "GLSL version: " << b_end << m_glsl_version << line_end; - if (extensions) - { + if (extensions) { std::vector extensions_list; std::string extensions_str = gl_get_string_safe(GL_EXTENSIONS, ""); boost::split(extensions_list, extensions_str, boost::is_any_of(" "), boost::token_compress_off); - if (!extensions_list.empty()) - { + if (!extensions_list.empty()) { out << h2_start << "Installed extensions:" << h2_end << line_end; std::sort(extensions_list.begin(), extensions_list.end()); - for (const std::string& ext : extensions_list) - { + for (const std::string& ext : extensions_list) { out << ext << line_end; } } @@ -304,8 +303,7 @@ wxGLCanvas* OpenGLManager::create_wxglcanvas(wxWindow& parent) 0 }; - if (s_multisample == EMultisampleState::Unknown) - { + if (s_multisample == EMultisampleState::Unknown) { detect_multisample(attribList); // // debug output // std::cout << "Multisample " << (can_multisample() ? "enabled" : "disabled") << std::endl; diff --git a/src/slic3r/GUI/OpenGLManager.hpp b/src/slic3r/GUI/OpenGLManager.hpp index 5f8cd7959c..ca9452db66 100644 --- a/src/slic3r/GUI/OpenGLManager.hpp +++ b/src/slic3r/GUI/OpenGLManager.hpp @@ -22,14 +22,14 @@ public: class GLInfo { - mutable bool m_detected{ false }; - mutable int m_max_tex_size{ 0 }; - mutable float m_max_anisotropy{ 0.0f }; + bool m_detected{ false }; + int m_max_tex_size{ 0 }; + float m_max_anisotropy{ 0.0f }; - mutable std::string m_version; - mutable std::string m_glsl_version; - mutable std::string m_vendor; - mutable std::string m_renderer; + std::string m_version; + std::string m_glsl_version; + std::string m_vendor; + std::string m_renderer; public: GLInfo() = default; From 0e3090fb28f2fb672a73747ed1d666ed756e4e5f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 20 Apr 2021 12:16:55 +0200 Subject: [PATCH 279/285] Removed mutable members from class GLCanvas3D --- src/slic3r/GUI/GLCanvas3D.cpp | 164 +++++++++++++++++----------------- src/slic3r/GUI/GLCanvas3D.hpp | 40 ++++----- 2 files changed, 98 insertions(+), 106 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index fa3d96420b..97038723bb 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3528,7 +3528,7 @@ Vec2d GLCanvas3D::get_local_mouse_position() const void GLCanvas3D::set_tooltip(const std::string& tooltip) const { if (m_canvas != nullptr) - m_tooltip.set_text(tooltip); + const_cast(&m_tooltip)->set_text(tooltip); } void GLCanvas3D::do_move(const std::string& snapshot_type) @@ -3545,22 +3545,19 @@ void GLCanvas3D::do_move(const std::string& snapshot_type) Selection::EMode selection_mode = m_selection.get_mode(); - for (const GLVolume* v : m_volumes.volumes) - { + for (const GLVolume* v : m_volumes.volumes) { int object_idx = v->object_idx(); int instance_idx = v->instance_idx(); int volume_idx = v->volume_idx(); std::pair done_id(object_idx, instance_idx); - if ((0 <= object_idx) && (object_idx < (int)m_model->objects.size())) - { + if (0 <= object_idx && object_idx < (int)m_model->objects.size()) { done.insert(done_id); // Move instances/volumes ModelObject* model_object = m_model->objects[object_idx]; - if (model_object != nullptr) - { + if (model_object != nullptr) { if (selection_mode == Selection::Instance) model_object->instances[instance_idx]->set_offset(v->get_instance_offset()); else if (selection_mode == Selection::Volume) @@ -3576,8 +3573,7 @@ void GLCanvas3D::do_move(const std::string& snapshot_type) } // Fixes sinking/flying instances - for (const std::pair& i : done) - { + for (const std::pair& i : done) { ModelObject* m = m_model->objects[i.first]; Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second)); m_selection.translate(i.first, i.second, shift); @@ -3936,13 +3932,13 @@ bool GLCanvas3D::_render_undo_redo_stack(const bool is_undo, float pos_x) const em *= m_retina_helper->get_scale_factor(); #endif - if (imgui->undo_redo_list(ImVec2(18 * em, 26 * em), is_undo, &string_getter, hovered, selected, m_mouse_wheel)) - m_imgui_undo_redo_hovered_pos = hovered; + int* mouse_wheel = const_cast(&m_mouse_wheel); + if (imgui->undo_redo_list(ImVec2(18 * em, 26 * em), is_undo, &string_getter, hovered, selected, *mouse_wheel)) + *const_cast(&m_imgui_undo_redo_hovered_pos) = hovered; else - m_imgui_undo_redo_hovered_pos = -1; + *const_cast(&m_imgui_undo_redo_hovered_pos) = -1; - if (selected >= 0) - { + if (selected >= 0) { is_undo ? wxGetApp().plater()->undo_to(selected) : wxGetApp().plater()->redo_to(selected); action_taken = true; } @@ -3983,9 +3979,10 @@ bool GLCanvas3D::_render_search_list(float pos_x) const char *s = new char[255]; strcpy(s, search_line.empty() ? _u8L("Enter a search term").c_str() : search_line.c_str()); - imgui->search_list(ImVec2(45 * em, 30 * em), &search_string_getter, s, - sidebar.get_searcher().view_params, - selected, edited, m_mouse_wheel, wxGetApp().is_localized()); + int* mouse_wheel = const_cast(&m_mouse_wheel); + imgui->search_list(ImVec2(45 * em, 30 * em), &search_string_getter, s, + sidebar.get_searcher().view_params, + selected, edited, *mouse_wheel, wxGetApp().is_localized()); search_line = s; delete [] s; @@ -4844,8 +4841,10 @@ void GLCanvas3D::_refresh_if_shown_on_screen() void GLCanvas3D::_picking_pass() const { + std::vector* hover_volume_idxs = const_cast*>(&m_hover_volume_idxs); + if (m_picking_enabled && !m_mouse.dragging && m_mouse.position != Vec2d(DBL_MAX, DBL_MAX)) { - m_hover_volume_idxs.clear(); + hover_volume_idxs->clear(); // Render the object for picking. // FIXME This cannot possibly work in a multi - sampled context as the color gets mangled by the anti - aliasing. @@ -4860,9 +4859,10 @@ void GLCanvas3D::_picking_pass() const glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); - m_camera_clipping_plane = m_gizmos.get_clipping_plane(); - if (m_camera_clipping_plane.is_active()) { - ::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)m_camera_clipping_plane.get_data()); + ClippingPlane* camera_clipping_plane = const_cast(&m_camera_clipping_plane); + *camera_clipping_plane = m_gizmos.get_clipping_plane(); + if (camera_clipping_plane->is_active()) { + ::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)camera_clipping_plane->get_data()); ::glEnable(GL_CLIP_PLANE0); } _render_volumes_for_picking(); @@ -4888,11 +4888,11 @@ void GLCanvas3D::_picking_pass() const if (0 <= volume_id && volume_id < (int)m_volumes.volumes.size()) { // do not add the volume id if any gizmo is active and CTRL is pressed if (m_gizmos.get_current_type() == GLGizmosManager::EType::Undefined || !wxGetKeyState(WXK_CONTROL)) - m_hover_volume_idxs.emplace_back(volume_id); - m_gizmos.set_hover_id(-1); + hover_volume_idxs->emplace_back(volume_id); + const_cast(&m_gizmos)->set_hover_id(-1); } else - m_gizmos.set_hover_id(inside && (unsigned int)volume_id <= GLGizmoBase::BASE_ID ? ((int)GLGizmoBase::BASE_ID - volume_id) : -1); + const_cast(&m_gizmos)->set_hover_id(inside && (unsigned int)volume_id <= GLGizmoBase::BASE_ID ? ((int)GLGizmoBase::BASE_ID - volume_id) : -1); _update_volumes_hover_state(); } @@ -4900,12 +4900,11 @@ void GLCanvas3D::_picking_pass() const void GLCanvas3D::_rectangular_selection_picking_pass() const { - m_gizmos.set_hover_id(-1); + const_cast(&m_gizmos)->set_hover_id(-1); std::set idxs; - if (m_picking_enabled) - { + if (m_picking_enabled) { if (m_multisample_allowed) // This flag is often ignored by NVIDIA drivers if rendering into a screen buffer. glsafe(::glDisable(GL_MULTISAMPLE)); @@ -4926,8 +4925,7 @@ void GLCanvas3D::_rectangular_selection_picking_pass() const int left = (int)m_rectangle_selection.get_left(); int top = get_canvas_size().get_height() - (int)m_rectangle_selection.get_top(); - if ((left >= 0) && (top >= 0)) - { + if (left >= 0 && top >= 0) { #define USE_PARALLEL 1 #if USE_PARALLEL struct Pixel @@ -4947,7 +4945,7 @@ void GLCanvas3D::_rectangular_selection_picking_pass() const for (size_t i = range.begin(); i < range.end(); ++i) if (frame[i].valid()) { int volume_id = frame[i].id(); - if ((0 <= volume_id) && (volume_id < (int)m_volumes.volumes.size())) { + if (0 <= volume_id && volume_id < (int)m_volumes.volumes.size()) { mutex.lock(); idxs.insert(volume_id); mutex.unlock(); @@ -4962,14 +4960,14 @@ void GLCanvas3D::_rectangular_selection_picking_pass() const { int px_id = 4 * i; int volume_id = frame[px_id] + (frame[px_id + 1] << 8) + (frame[px_id + 2] << 16); - if ((0 <= volume_id) && (volume_id < (int)m_volumes.volumes.size())) + if (0 <= volume_id && volume_id < (int)m_volumes.volumes.size()) idxs.insert(volume_id); } #endif // USE_PARALLEL } } - m_hover_volume_idxs.assign(idxs.begin(), idxs.end()); + const_cast*>(&m_hover_volume_idxs)->assign(idxs.begin(), idxs.end()); _update_volumes_hover_state(); } @@ -5062,7 +5060,9 @@ void GLCanvas3D::_render_objects() const glsafe(::glEnable(GL_DEPTH_TEST)); - m_camera_clipping_plane = m_gizmos.get_clipping_plane(); + ClippingPlane* camera_clipping_plane = const_cast(&m_camera_clipping_plane); + GLVolumeCollection* volumes = const_cast(&m_volumes); + *camera_clipping_plane = m_gizmos.get_clipping_plane(); if (m_picking_enabled) { // Update the layer editing selection to the first object selected, update the current object maximum Z. @@ -5070,17 +5070,17 @@ void GLCanvas3D::_render_objects() const if (m_config != nullptr) { const BoundingBoxf3& bed_bb = wxGetApp().plater()->get_bed().get_bounding_box(false); - m_volumes.set_print_box((float)bed_bb.min(0) - BedEpsilon, (float)bed_bb.min(1) - BedEpsilon, 0.0f, (float)bed_bb.max(0) + BedEpsilon, (float)bed_bb.max(1) + BedEpsilon, (float)m_config->opt_float("max_print_height")); - m_volumes.check_outside_state(m_config, nullptr); + volumes->set_print_box((float)bed_bb.min(0) - BedEpsilon, (float)bed_bb.min(1) - BedEpsilon, 0.0f, (float)bed_bb.max(0) + BedEpsilon, (float)bed_bb.max(1) + BedEpsilon, (float)m_config->opt_float("max_print_height")); + volumes->check_outside_state(m_config, nullptr); } } if (m_use_clipping_planes) - m_volumes.set_z_range(-m_clipping_planes[0].get_data()[3], m_clipping_planes[1].get_data()[3]); + volumes->set_z_range(-m_clipping_planes[0].get_data()[3], m_clipping_planes[1].get_data()[3]); else - m_volumes.set_z_range(-FLT_MAX, FLT_MAX); + volumes->set_z_range(-FLT_MAX, FLT_MAX); - m_volumes.set_clipping_plane(m_camera_clipping_plane.get_data()); + volumes->set_clipping_plane(camera_clipping_plane->get_data()); GLShaderProgram* shader = wxGetApp().get_shader("gouraud"); if (shader != nullptr) { @@ -5088,16 +5088,16 @@ void GLCanvas3D::_render_objects() const if (m_picking_enabled && !m_gizmos.is_dragging() && m_layers_editing.is_enabled() && (m_layers_editing.last_object_id != -1) && (m_layers_editing.object_max_z() > 0.0f)) { int object_id = m_layers_editing.last_object_id; - m_volumes.render(GLVolumeCollection::Opaque, false, wxGetApp().plater()->get_camera().get_view_matrix(), [object_id](const GLVolume& volume) { + volumes->render(GLVolumeCollection::Opaque, false, wxGetApp().plater()->get_camera().get_view_matrix(), [object_id](const GLVolume& volume) { // Which volume to paint without the layer height profile shader? return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id); }); // Let LayersEditing handle rendering of the active object using the layer height profile shader. - m_layers_editing.render_volumes(*this, this->m_volumes); + m_layers_editing.render_volumes(*this, *volumes); } else { - // do not cull backfaces to show broken geometry, if any - m_volumes.render(GLVolumeCollection::Opaque, m_picking_enabled, wxGetApp().plater()->get_camera().get_view_matrix(), [this](const GLVolume& volume) { - return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0); + // do not cull backfaces to show broken geometry, if any + volumes->render(GLVolumeCollection::Opaque, m_picking_enabled, wxGetApp().plater()->get_camera().get_view_matrix(), [this](const GLVolume& volume) { + return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0); }); } @@ -5115,11 +5115,11 @@ void GLCanvas3D::_render_objects() const } } - m_volumes.render(GLVolumeCollection::Transparent, false, wxGetApp().plater()->get_camera().get_view_matrix()); + volumes->render(GLVolumeCollection::Transparent, false, wxGetApp().plater()->get_camera().get_view_matrix()); shader->stop_using(); } - m_camera_clipping_plane = ClippingPlane::ClipsNothing(); + *camera_clipping_plane = ClippingPlane::ClipsNothing(); } void GLCanvas3D::_render_gcode() const @@ -5160,13 +5160,13 @@ void GLCanvas3D::_check_and_update_toolbar_icon_scale() const GLToolbar& collapse_toolbar = wxGetApp().plater()->get_collapse_toolbar(); #if ENABLE_RETINA_GL const float sc = m_retina_helper->get_scale_factor() * scale; - m_main_toolbar.set_scale(sc); - m_undoredo_toolbar.set_scale(sc); + const_cast(&m_main_toolbar)->set_scale(sc); + const_cast(&m_undoredo_toolbar)->set_scale(sc); collapse_toolbar.set_scale(sc); size *= m_retina_helper->get_scale_factor(); #else - m_main_toolbar.set_icons_size(size); - m_undoredo_toolbar.set_icons_size(size); + const_cast(&m_main_toolbar)->set_icons_size(size); + const_cast(&m_undoredo_toolbar)->set_icons_size(size); collapse_toolbar.set_icons_size(size); #endif // ENABLE_RETINA_GL @@ -5214,13 +5214,13 @@ void GLCanvas3D::_render_overlays() const // to correctly place them #if ENABLE_RETINA_GL const float scale = m_retina_helper->get_scale_factor() * wxGetApp().toolbar_icon_scale(/*true*/); - m_main_toolbar.set_scale(scale); - m_undoredo_toolbar.set_scale(scale); + const_cast(&m_main_toolbar)->set_scale(scale); + const_cast(&m_undoredo_toolbar)->set_scale(scale); wxGetApp().plater()->get_collapse_toolbar().set_scale(scale); #else const float size = int(GLToolbar::Default_Icons_Size * wxGetApp().toolbar_icon_scale(/*true*/)); - m_main_toolbar.set_icons_size(size); - m_undoredo_toolbar.set_icons_size(size); + const_cast(&m_main_toolbar)->set_icons_size(size); + const_cast(&m_undoredo_toolbar)->set_icons_size(size); wxGetApp().plater()->get_collapse_toolbar().set_icons_size(size); #endif // ENABLE_RETINA_GL @@ -5295,12 +5295,12 @@ void GLCanvas3D::_render_gizmos_overlay() const #if ENABLE_RETINA_GL // m_gizmos.set_overlay_scale(m_retina_helper->get_scale_factor()); const float scale = m_retina_helper->get_scale_factor()*wxGetApp().toolbar_icon_scale(); - m_gizmos.set_overlay_scale(scale); //! #ys_FIXME_experiment + const_cast(&m_gizmos)->set_overlay_scale(scale); //! #ys_FIXME_experiment #else // m_gizmos.set_overlay_scale(m_canvas->GetContentScaleFactor()); // m_gizmos.set_overlay_scale(wxGetApp().em_unit()*0.1f); - const float size = int(GLGizmosManager::Default_Icons_Size*wxGetApp().toolbar_icon_scale()); - m_gizmos.set_overlay_icon_size(size); //! #ys_FIXME_experiment + const float size = int(GLGizmosManager::Default_Icons_Size * wxGetApp().toolbar_icon_scale()); + const_cast(&m_gizmos)->set_overlay_icon_size(size); //! #ys_FIXME_experiment #endif /* __WXMSW__ */ m_gizmos.render_overlay(); @@ -5319,8 +5319,9 @@ void GLCanvas3D::_render_main_toolbar() const float collapse_toolbar_width = collapse_toolbar.is_enabled() ? collapse_toolbar.get_width() : 0.0f; float left = -0.5f * (m_main_toolbar.get_width() + m_undoredo_toolbar.get_width() + collapse_toolbar_width) * inv_zoom; - m_main_toolbar.set_position(top, left); - m_main_toolbar.render(*this); + GLToolbar* main_toolbar = const_cast(&m_main_toolbar); + main_toolbar->set_position(top, left); + main_toolbar->render(*this); } void GLCanvas3D::_render_undoredo_toolbar() const @@ -5335,8 +5336,10 @@ void GLCanvas3D::_render_undoredo_toolbar() const const GLToolbar& collapse_toolbar = wxGetApp().plater()->get_collapse_toolbar(); float collapse_toolbar_width = collapse_toolbar.is_enabled() ? collapse_toolbar.get_width() : 0.0f; float left = (m_main_toolbar.get_width() - 0.5f * (m_main_toolbar.get_width() + m_undoredo_toolbar.get_width() + collapse_toolbar_width)) * inv_zoom; - m_undoredo_toolbar.set_position(top, left); - m_undoredo_toolbar.render(*this); + + GLToolbar* undoredo_toolbar = const_cast(&m_undoredo_toolbar); + undoredo_toolbar->set_position(top, left); + undoredo_toolbar->render(*this); } void GLCanvas3D::_render_collapse_toolbar() const @@ -5427,20 +5430,21 @@ void GLCanvas3D::_render_sla_slices() const if (!obj->is_step_done(slaposSliceSupports)) continue; - SlaCap::ObjectIdToTrianglesMap::iterator it_caps_bottom = m_sla_caps[0].triangles.find(i); - SlaCap::ObjectIdToTrianglesMap::iterator it_caps_top = m_sla_caps[1].triangles.find(i); + SlaCap* sla_caps = const_cast(m_sla_caps); + SlaCap::ObjectIdToTrianglesMap::iterator it_caps_bottom = sla_caps[0].triangles.find(i); + SlaCap::ObjectIdToTrianglesMap::iterator it_caps_top = sla_caps[1].triangles.find(i); { - if (it_caps_bottom == m_sla_caps[0].triangles.end()) - it_caps_bottom = m_sla_caps[0].triangles.emplace(i, SlaCap::Triangles()).first; - if (! m_sla_caps[0].matches(clip_min_z)) { - m_sla_caps[0].z = clip_min_z; + if (it_caps_bottom == sla_caps[0].triangles.end()) + it_caps_bottom = sla_caps[0].triangles.emplace(i, SlaCap::Triangles()).first; + if (!sla_caps[0].matches(clip_min_z)) { + sla_caps[0].z = clip_min_z; it_caps_bottom->second.object.clear(); it_caps_bottom->second.supports.clear(); } - if (it_caps_top == m_sla_caps[1].triangles.end()) - it_caps_top = m_sla_caps[1].triangles.emplace(i, SlaCap::Triangles()).first; - if (! m_sla_caps[1].matches(clip_max_z)) { - m_sla_caps[1].z = clip_max_z; + if (it_caps_top == sla_caps[1].triangles.end()) + it_caps_top = sla_caps[1].triangles.emplace(i, SlaCap::Triangles()).first; + if (!sla_caps[1].matches(clip_max_z)) { + sla_caps[1].z = clip_max_z; it_caps_top->second.object.clear(); it_caps_top->second.supports.clear(); } @@ -5546,7 +5550,7 @@ void GLCanvas3D::_update_volumes_hover_state() const if (alt_pressed && (shift_pressed || ctrl_pressed)) { // illegal combinations of keys - m_hover_volume_idxs.clear(); + const_cast*>(&m_hover_volume_idxs)->clear(); return; } @@ -5570,7 +5574,7 @@ void GLCanvas3D::_update_volumes_hover_state() const if (hover_modifiers_only && !hover_from_single_instance) { // do not allow to select volumes from different instances - m_hover_volume_idxs.clear(); + const_cast*>(&m_hover_volume_idxs)->clear(); return; } @@ -5591,23 +5595,15 @@ void GLCanvas3D::_update_volumes_hover_state() const (deselect && !m_selection.is_single_full_instance() && (volume.object_idx() == m_selection.get_object_idx()) && (volume.instance_idx() == m_selection.get_instance_idx())) ); - if (as_volume) { - if (deselect) - volume.hover = GLVolume::HS_Deselect; - else - volume.hover = GLVolume::HS_Select; - } + if (as_volume) + volume.hover = deselect ? GLVolume::HS_Deselect : GLVolume::HS_Select; else { int object_idx = volume.object_idx(); int instance_idx = volume.instance_idx(); for (GLVolume* v : m_volumes.volumes) { - if (v->object_idx() == object_idx && v->instance_idx() == instance_idx) { - if (deselect) - v->hover = GLVolume::HS_Deselect; - else - v->hover = GLVolume::HS_Select; - } + if (v->object_idx() == object_idx && v->instance_idx() == instance_idx) + v->hover = deselect ? GLVolume::HS_Deselect : GLVolume::HS_Select; } } } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index f4d862b66c..9e9a2501e1 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -449,13 +449,13 @@ private: wxTimer m_timer; LayersEditing m_layers_editing; Mouse m_mouse; - mutable GLGizmosManager m_gizmos; - mutable GLToolbar m_main_toolbar; - mutable GLToolbar m_undoredo_toolbar; + GLGizmosManager m_gizmos; + GLToolbar m_main_toolbar; + GLToolbar m_undoredo_toolbar; ClippingPlane m_clipping_planes[2]; - mutable ClippingPlane m_camera_clipping_plane; + ClippingPlane m_camera_clipping_plane; bool m_use_clipping_planes; - mutable SlaCap m_sla_caps[2]; + SlaCap m_sla_caps[2]; std::string m_sidebar_field; // when true renders an extra frame by not resetting m_dirty to false // see request_extra_frame() @@ -463,7 +463,7 @@ private: int m_extra_frame_requested_delayed { std::numeric_limits::max() }; bool m_event_handlers_bound{ false }; - mutable GLVolumeCollection m_volumes; + GLVolumeCollection m_volumes; GCodeViewer m_gcode_viewer; RenderTimer m_render_timer; @@ -478,7 +478,6 @@ private: bool m_dirty; bool m_initialized; bool m_apply_zoom_to_volumes_filter; - mutable std::vector m_hover_volume_idxs; bool m_picking_enabled; bool m_moving_enabled; bool m_dynamic_background_enabled; @@ -487,6 +486,7 @@ private: bool m_tab_down; ECursorType m_cursor_type; GLSelectionRectangle m_rectangle_selection; + std::vector m_hover_volume_idxs; // Following variable is obsolete and it should be safe to remove it. // I just don't want to do it now before a release (Lukas Matena 24.3.2019) @@ -504,13 +504,13 @@ private: RenderStats m_render_stats; #endif // ENABLE_RENDER_STATISTICS - mutable int m_imgui_undo_redo_hovered_pos{ -1 }; - mutable int m_mouse_wheel {0}; + int m_imgui_undo_redo_hovered_pos{ -1 }; + int m_mouse_wheel{ 0 }; int m_selected_extruder; Labels m_labels; - mutable Tooltip m_tooltip; - mutable bool m_tooltip_enabled{ true }; + Tooltip m_tooltip; + bool m_tooltip_enabled{ true }; Slope m_slope; ArrangeSettings m_arrange_settings_fff, m_arrange_settings_sla, @@ -519,8 +519,7 @@ private: PrinterTechnology current_printer_technology() const; template - static auto & get_arrange_settings(Self *self) - { + static auto & get_arrange_settings(Self *self) { PrinterTechnology ptech = self->current_printer_technology(); auto *ptr = &self->m_arrange_settings_fff; @@ -529,11 +528,10 @@ private: ptr = &self->m_arrange_settings_sla; } else if (ptech == ptFFF) { auto co_opt = self->m_config->template option("complete_objects"); - if (co_opt && co_opt->value) { + if (co_opt && co_opt->value) ptr = &self->m_arrange_settings_fff_seq_print; - } else { + else ptr = &self->m_arrange_settings_fff; - } } return *ptr; @@ -715,10 +713,9 @@ public: double m_rotation = 0.; BoundingBoxf m_bb; friend class GLCanvas3D; - public: - - inline operator bool() const - { + + public: + inline operator bool() const { return !std::isnan(m_pos.x()) && !std::isnan(m_pos.y()); } @@ -763,8 +760,7 @@ public: void use_slope(bool use) { m_slope.use(use); } void set_slope_normal_angle(float angle_in_deg) { m_slope.set_normal_angle(angle_in_deg); } - ArrangeSettings get_arrange_settings() const - { + ArrangeSettings get_arrange_settings() const { const ArrangeSettings &settings = get_arrange_settings(this); ArrangeSettings ret = settings; if (&settings == &m_arrange_settings_fff_seq_print) { From 6be2a1be2c9b053b4cd994f816c76a3ef722deb1 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 20 Apr 2021 12:55:23 +0200 Subject: [PATCH 280/285] Removed mutable members from class GLVolume --- src/slic3r/GUI/3DScene.cpp | 42 ++++++++++++++++++++------------------ src/slic3r/GUI/3DScene.hpp | 12 +++++------ 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 6c226cd74d..ba62576f23 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -421,20 +421,24 @@ const BoundingBoxf3& GLVolume::transformed_bounding_box() const const BoundingBoxf3& box = bounding_box(); assert(box.defined || box.min(0) >= box.max(0) || box.min(1) >= box.max(1) || box.min(2) >= box.max(2)); - if (m_transformed_bounding_box_dirty) - { - m_transformed_bounding_box = box.transformed(world_matrix()); - m_transformed_bounding_box_dirty = false; + BoundingBoxf3* transformed_bounding_box = const_cast(&m_transformed_bounding_box); + bool* transformed_bounding_box_dirty = const_cast(&m_transformed_bounding_box_dirty); + if (*transformed_bounding_box_dirty) { + *transformed_bounding_box = box.transformed(world_matrix()); + *transformed_bounding_box_dirty = false; } - - return m_transformed_bounding_box; + return *transformed_bounding_box; } const BoundingBoxf3& GLVolume::transformed_convex_hull_bounding_box() const { - if (m_transformed_convex_hull_bounding_box_dirty) - m_transformed_convex_hull_bounding_box = this->transformed_convex_hull_bounding_box(world_matrix()); - return m_transformed_convex_hull_bounding_box; + BoundingBoxf3* transformed_convex_hull_bounding_box = const_cast(&m_transformed_convex_hull_bounding_box); + bool* transformed_convex_hull_bounding_box_dirty = const_cast(&m_transformed_convex_hull_bounding_box_dirty); + if (*transformed_convex_hull_bounding_box_dirty) { + *transformed_convex_hull_bounding_box = this->transformed_convex_hull_bounding_box(world_matrix()); + *transformed_convex_hull_bounding_box_dirty = false; + } + return *transformed_convex_hull_bounding_box; } BoundingBoxf3 GLVolume::transformed_convex_hull_bounding_box(const Transform3d &trafo) const @@ -795,7 +799,7 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab glsafe(::glDisable(GL_BLEND)); } -bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state) +bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state) const { if (config == nullptr) return false; @@ -805,7 +809,7 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M return false; BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values)); - BoundingBoxf3 print_volume(Vec3d(unscale(bed_box_2D.min(0)), unscale(bed_box_2D.min(1)), 0.0), Vec3d(unscale(bed_box_2D.max(0)), unscale(bed_box_2D.max(1)), config->opt_float("max_print_height"))); + BoundingBoxf3 print_volume({ unscale(bed_box_2D.min(0)), unscale(bed_box_2D.min(1)), 0.0 }, { unscale(bed_box_2D.max(0)), unscale(bed_box_2D.max(1)), config->opt_float("max_print_height") }); // Allow the objects to protrude below the print bed print_volume.min(2) = -1e10; print_volume.min(0) -= BedEpsilon; @@ -817,9 +821,8 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M bool contained_min_one = false; - for (GLVolume* volume : this->volumes) - { - if ((volume == nullptr) || volume->is_modifier || (volume->is_wipe_tower && !volume->shader_outside_printer_detection_enabled) || ((volume->composite_id.volume_id < 0) && !volume->shader_outside_printer_detection_enabled)) + for (GLVolume* volume : this->volumes) { + if (volume == nullptr || volume->is_modifier || (volume->is_wipe_tower && !volume->shader_outside_printer_detection_enabled) || (volume->composite_id.volume_id < 0 && !volume->shader_outside_printer_detection_enabled)) continue; const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box(); @@ -832,10 +835,10 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M if (contained) contained_min_one = true; - if ((state == ModelInstancePVS_Inside) && volume->is_outside) + if (state == ModelInstancePVS_Inside && volume->is_outside) state = ModelInstancePVS_Fully_Outside; - if ((state == ModelInstancePVS_Fully_Outside) && volume->is_outside && print_volume.intersects(bb)) + if (state == ModelInstancePVS_Fully_Outside && volume->is_outside && print_volume.intersects(bb)) state = ModelInstancePVS_Partly_Outside; } @@ -845,7 +848,7 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M return contained_min_one; } -bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, bool& partlyOut, bool& fullyOut) +bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, bool& partlyOut, bool& fullyOut) const { if (config == nullptr) return false; @@ -867,9 +870,8 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, b partlyOut = false; fullyOut = false; - for (GLVolume* volume : this->volumes) - { - if ((volume == nullptr) || volume->is_modifier || (volume->is_wipe_tower && !volume->shader_outside_printer_detection_enabled) || ((volume->composite_id.volume_id < 0) && !volume->shader_outside_printer_detection_enabled)) + for (GLVolume* volume : this->volumes) { + if (volume == nullptr || volume->is_modifier || (volume->is_wipe_tower && !volume->shader_outside_printer_detection_enabled) || (volume->composite_id.volume_id < 0 && !volume->shader_outside_printer_detection_enabled)) continue; const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box(); diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 2ae2a36b29..25c5443cda 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -267,15 +267,15 @@ private: // Shift in z required by sla supports+pad double m_sla_shift_z; // Bounding box of this volume, in unscaled coordinates. - mutable BoundingBoxf3 m_transformed_bounding_box; + BoundingBoxf3 m_transformed_bounding_box; // Whether or not is needed to recalculate the transformed bounding box. - mutable bool m_transformed_bounding_box_dirty; + bool m_transformed_bounding_box_dirty; // Convex hull of the volume, if any. std::shared_ptr m_convex_hull; // Bounding box of this volume, in unscaled coordinates. - mutable BoundingBoxf3 m_transformed_convex_hull_bounding_box; + BoundingBoxf3 m_transformed_convex_hull_bounding_box; // Whether or not is needed to recalculate the transformed convex hull bounding box. - mutable bool m_transformed_convex_hull_bounding_box_dirty; + bool m_transformed_convex_hull_bounding_box_dirty; public: // Color of the triangles / quads held by this volume. @@ -568,8 +568,8 @@ public: // returns true if all the volumes are completely contained in the print volume // returns the containment state in the given out_state, if non-null - bool check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state); - bool check_outside_state(const DynamicPrintConfig* config, bool& partlyOut, bool& fullyOut); + bool check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state) const; + bool check_outside_state(const DynamicPrintConfig* config, bool& partlyOut, bool& fullyOut) const; void reset_outside_state(); void update_colors_by_extruder(const DynamicPrintConfig* config); From d50c2872bd30dd120d151d0175854f37de38c17e Mon Sep 17 00:00:00 2001 From: Pascal de Bruijn Date: Tue, 20 Apr 2021 18:12:08 +0200 Subject: [PATCH 281/285] creality.ini: disable explicit ABL for CR6-SE (#6383) this is also better in line with Creality's intended behavior --- resources/profiles/Creality.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index 95ce8a0dce..3dab515ab5 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -969,7 +969,7 @@ printer_model = CR5PROH printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR5PROH\nPRINTER_HAS_BOWDEN [printer:Creality CR-6 SE] -inherits = *common*; *fastabl*; *pauseprint* +inherits = *common*; *pauseprint* bed_shape = 5x0,230x0,230x235,5x235 printer_model = CR6SE printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR6SE\nPRINTER_HAS_BOWDEN From 2cb268c9476b44e2a557302aba8e56df22b34c0b Mon Sep 17 00:00:00 2001 From: Pascal de Bruijn Date: Tue, 20 Apr 2021 18:13:21 +0200 Subject: [PATCH 282/285] Revert "creality.ini: Extrudr NX2 slightly lower temps" This reverts commit 41c56f2eb8a48ef5530938da08909365108f686d. --- resources/profiles/Creality.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index 3dab515ab5..8420e502e4 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -655,9 +655,9 @@ filament_spool_weight = 256 [filament:Extrudr PLA NX2 @CREALITY] inherits = *PLA* filament_vendor = Extrudr -temperature = 195 +temperature = 200 bed_temperature = 60 -first_layer_temperature = 200 +first_layer_temperature = 205 first_layer_bed_temperature = 60 filament_cost = 23.63 filament_density = 1.3 From b71fa0d6340ef42a7c11436099cb0c60ae319a71 Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Tue, 20 Apr 2021 20:35:49 +0200 Subject: [PATCH 283/285] Bumped up version to 0.0.16 --- resources/profiles/Creality.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index 8420e502e4..5248155250 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -5,7 +5,7 @@ name = Creality # Configuration version of this file. Config file will only be installed, if the config_version differs. # This means, the server may force the PrusaSlicer configuration to be downgraded. -config_version = 0.0.15 +config_version = 0.0.16 # Where to get the updates from? config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Creality/ # changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1% From 8b41285e310355cd2d9b8e6eee6ba3f2ad2bf87b Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Tue, 20 Apr 2021 20:38:27 +0200 Subject: [PATCH 284/285] 0.0.16 Updated CR6-SE start g-code. Added and updated filament profiles. --- resources/profiles/Creality.idx | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/profiles/Creality.idx b/resources/profiles/Creality.idx index 2833b8afbb..c1242f27ec 100644 --- a/resources/profiles/Creality.idx +++ b/resources/profiles/Creality.idx @@ -1,4 +1,5 @@ min_slic3r_version = 2.3.1-beta +0.0.16 Updated CR6-SE start g-code. Added and updated filament profiles. 0.0.15 Added new printer models, filament profiles. Various improvements. min_slic3r_version = 2.3.0-rc2 0.0.14 Optimized start g-code. Added filament profile. Various improvements. From 49928e131c8b8f86509e78d3e4e260036f86a7ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Wed, 21 Apr 2021 06:08:46 +0200 Subject: [PATCH 285/285] Added missing include (GCC 9.3) --- src/slic3r/GUI/PrintHostDialogs.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index 762450c537..935746a64e 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include