From ffc8a3e307ec44d1ed14dbb36aa75752d257ec7f Mon Sep 17 00:00:00 2001 From: Argo <52103738+Argolein@users.noreply.github.com> Date: Tue, 10 Feb 2026 02:12:30 +0100 Subject: [PATCH 1/5] Adds (AMS) filament sync options: Only sync color / sync all (#12169) This PR adds a new filament sync mode setting for device-based filament synchronization. Users can now choose between syncing both filament preset + color (current behavior) or syncing color only, so calibrated local filament profiles are preserved while still updating slot colors from the printer. It also includes small UI improvements for the new preference entry and sync status messaging. image --- src/libslic3r/AppConfig.cpp | 2 + src/libslic3r/PresetBundle.cpp | 68 +++++++++++++++++++++++++++- src/libslic3r/PresetBundle.hpp | 2 +- src/slic3r/GUI/Plater.cpp | 36 ++++++++++----- src/slic3r/GUI/Preferences.cpp | 10 ++++ src/slic3r/GUI/SyncAmsInfoDialog.cpp | 4 +- 6 files changed, 106 insertions(+), 16 deletions(-) diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index a9f04aae19..64d4bbc89e 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -220,6 +220,8 @@ void AppConfig::set_defaults() set_bool("enable_merge_color_by_sync_ams", false); if (get("ams_sync_match_full_use_color_dist").empty()) set_bool("ams_sync_match_full_use_color_dist", false); + if (get("sync_ams_filament_mode").empty()) + set("sync_ams_filament_mode", "0"); // 0: filament+color, 1: color only if (get("camera_orbit_mult").empty()) set("camera_orbit_mult", "1.0"); diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 576080f51e..d5244b6bc0 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -2338,7 +2338,7 @@ void PresetBundle::get_ams_cobox_infos(AMSComboInfo& combox_info) } } -unsigned int PresetBundle::sync_ams_list(std::vector> &unknowns, bool use_map, std::map &maps,bool enable_append, MergeFilamentInfo &merge_info) +unsigned int PresetBundle::sync_ams_list(std::vector> &unknowns, bool use_map, std::map &maps, bool enable_append, MergeFilamentInfo &merge_info, bool color_only) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "use_map:" << use_map << " enable_append:" << enable_append; std::vector ams_filament_presets; @@ -2489,7 +2489,71 @@ unsigned int PresetBundle::sync_ams_list(std::vector("filament_colour"); ConfigOptionStrings *filament_color_type = project_config.option("filament_colour_type"); ConfigOptionInts * filament_map = project_config.option("filament_map"); - if (use_map) { + if (color_only) { + auto get_map_index = [&ams_infos](const std::vector &infos, const AMSMapInfo &temp) { + for (int i = 0; i < infos.size(); i++) { + if (infos[i].slot_id == temp.slot_id && infos[i].ams_id == temp.ams_id) { + ams_infos[i].is_map = true; + return i; + } + } + return -1; + }; + + auto exist_colors = filament_color->values; + std::vector> exist_multi_color_filment(exist_colors.size()); + for (size_t i = 0; i < exist_colors.size(); i++) { + exist_multi_color_filment[i] = {exist_colors[i]}; + } + + ConfigOptionStrings *project_multi_color = project_config.option("filament_multi_colour"); + if (project_multi_color) { + for (size_t i = 0; i < std::min(exist_multi_color_filment.size(), project_multi_color->values.size()); i++) { + std::vector colors = split_string(project_multi_color->values[i], ' '); + if (!colors.empty()) { + exist_multi_color_filment[i] = colors; + } + } + } + + bool mapped_any = false; + if (use_map && !maps.empty()) { + for (size_t i = 0; i < exist_colors.size(); i++) { + if (maps.find(i) == maps.end()) { + continue; + } + int valid_index = get_map_index(ams_array_maps, maps[i]); + if (valid_index >= 0 && valid_index < int(ams_filament_colors.size()) && !ams_filament_colors[valid_index].empty()) { + exist_colors[i] = ams_filament_colors[valid_index]; + mapped_any = true; + if (valid_index < int(ams_multi_color_filment.size()) && !ams_multi_color_filment[valid_index].empty()) { + exist_multi_color_filment[i] = ams_multi_color_filment[valid_index]; + } else { + exist_multi_color_filment[i] = {ams_filament_colors[valid_index]}; + } + } + } + } + // Fallback to index-based color sync if no mapping was applied. + if (!use_map || maps.empty() || !mapped_any) { + size_t sync_count = std::min(exist_colors.size(), ams_filament_colors.size()); + for (size_t i = 0; i < sync_count; i++) { + if (ams_filament_colors[i].empty()) { + continue; + } + exist_colors[i] = ams_filament_colors[i]; + if (i < ams_multi_color_filment.size() && !ams_multi_color_filment[i].empty()) { + exist_multi_color_filment[i] = ams_multi_color_filment[i]; + } else { + exist_multi_color_filment[i] = {ams_filament_colors[i]}; + } + } + } + + filament_color->values = exist_colors; + ams_multi_color_filment = exist_multi_color_filment; + merge_info.merges.clear(); + } else if (use_map) { auto check_has_merge_info = [](std::map &maps, MergeFilamentInfo &merge_info, int exist_colors_size) { std::set done; for (auto it_i = maps.begin(); it_i != maps.end(); ++it_i) { diff --git a/src/libslic3r/PresetBundle.hpp b/src/libslic3r/PresetBundle.hpp index 3c6aa9e6e6..51eed5646f 100644 --- a/src/libslic3r/PresetBundle.hpp +++ b/src/libslic3r/PresetBundle.hpp @@ -173,7 +173,7 @@ public: void update_num_filaments(unsigned int to_del_flament_id); void get_ams_cobox_infos(AMSComboInfo &combox_info); - unsigned int sync_ams_list(std::vector> &unknowns, bool use_map, std::map &maps,bool enable_append, MergeFilamentInfo& merge_info); + unsigned int sync_ams_list(std::vector> &unknowns, bool use_map, std::map &maps, bool enable_append, MergeFilamentInfo &merge_info, bool color_only = false); //BBS: check whether this is the only edited filament bool is_the_only_edited_filament(unsigned int filament_index); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 7aa9a24cd3..2b955dc412 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3464,8 +3464,9 @@ void Sidebar::sync_ams_list(bool is_from_big_sync_btn) } MergeFilamentInfo merge_info; std::vector> unknowns; - auto enable_append = wxGetApp().app_config->get_bool("enable_append_color_by_sync_ams"); - auto n = wxGetApp().preset_bundle->sync_ams_list(unknowns, !sync_result.direct_sync, sync_result.sync_maps, enable_append, merge_info); + auto enable_append = wxGetApp().app_config->get_bool("enable_append_color_by_sync_ams"); + auto sync_color_only = wxGetApp().app_config->get("sync_ams_filament_mode") == "1"; + auto n = wxGetApp().preset_bundle->sync_ams_list(unknowns, !sync_result.direct_sync, sync_result.sync_maps, enable_append, merge_info, sync_color_only); wxString detail; for (auto & uk : unknowns) { auto tray_name = uk.first->opt_string("tray_name", 0u); @@ -3497,9 +3498,11 @@ void Sidebar::sync_ams_list(bool is_from_big_sync_btn) _L("Sync filaments with AMS"), wxOK); dlg.ShowModal(); } - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "on_filament_count_change"; - wxGetApp().plater()->on_filament_count_change(n); - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "finish on_filament_count_change"; + if (!sync_color_only) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "on_filament_count_change"; + wxGetApp().plater()->on_filament_count_change(n); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "finish on_filament_count_change"; + } for (auto& c : p->combos_filament) c->update(); // Expand filament list @@ -3523,14 +3526,23 @@ void Sidebar::sync_ams_list(bool is_from_big_sync_btn) } Layout(); - // Perform preset selection and list update first — these may rebuild combo widgets, - // which clears any badge state. Badges must be set AFTER these calls to persist. - wxGetApp().get_tab(Preset::TYPE_FILAMENT)->select_preset(wxGetApp().preset_bundle->filament_presets[0]); - wxGetApp().preset_bundle->export_selections(*wxGetApp().app_config); - update_dynamic_filament_list(); + // For full sync, preset selection/list update may rebuild combo widgets. + // For color-only, keep current presets untouched and refresh colors only. + if (!sync_color_only) { + wxGetApp().get_tab(Preset::TYPE_FILAMENT)->select_preset(wxGetApp().preset_bundle->filament_presets[0]); + wxGetApp().preset_bundle->export_selections(*wxGetApp().app_config); + update_dynamic_filament_list(); + } else { + wxGetApp().plater()->update_filament_colors_in_full_config(); + for (auto &c : p->combos_filament) + c->update(); + obj_list()->update_filament_colors(); + update_dynamic_filament_list(); + } - auto badge_combox_filament = [](PlaterPresetComboBox *c) { - auto tip = _L("Filament type and color information have been synchronized, but slot information is not included."); + auto badge_combox_filament = [sync_color_only](PlaterPresetComboBox *c) { + auto tip = sync_color_only ? _L("Only filament color information has been synchronized from printer.") : + _L("Filament type and color information have been synchronized, but slot information is not included."); c->SetToolTip(tip); c->ShowBadge(true); }; diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 28868d3ac5..98ec6e0707 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -1492,6 +1492,16 @@ void PreferencesDialog::create_items() SETTING_USE_ENCRYPTED_TOKEN_FILE); g_sizer->Add(item_token_storage); + //// ONLINE > Filament Sync Options + g_sizer->Add(create_item_title(_L("Filament Sync Options")), 1, wxEXPAND); + + auto item_filament_sync_mode = create_item_combobox( + _L("Filament sync mode"), + _L("Choose whether sync updates both filament preset and color, or only color."), + "sync_ams_filament_mode", + {_L("Filament & Color"), _L("Color only")}); + g_sizer->Add(item_filament_sync_mode); + //// ONLINE > Network plugin g_sizer->Add(create_item_title(_L("Network plugin")), 1, wxEXPAND); diff --git a/src/slic3r/GUI/SyncAmsInfoDialog.cpp b/src/slic3r/GUI/SyncAmsInfoDialog.cpp index 49f75d23d1..1e6f2256fe 100644 --- a/src/slic3r/GUI/SyncAmsInfoDialog.cpp +++ b/src/slic3r/GUI/SyncAmsInfoDialog.cpp @@ -3337,7 +3337,9 @@ FinishSyncAmsDialog::FinishSyncAmsDialog(InputInfo &input_info) 310, input_info.dialog_pos, 68, - _L("Successfully synchronized color and type of filament from printer."), + wxGetApp().app_config->get("sync_ams_filament_mode") == "1" ? + _L("Successfully synchronized filament color from printer.") : + _L("Successfully synchronized color and type of filament from printer."), _CTX(L_CONTEXT("OK", "FinishSyncAms"), "FinishSyncAms"), "", DisappearanceMode::TimedDisappearance) From 039a693b2521a7f380541396c6082802766efec6 Mon Sep 17 00:00:00 2001 From: paxx12 <245230251+paxx12@users.noreply.github.com> Date: Tue, 10 Feb 2026 03:54:57 +0100 Subject: [PATCH 2/5] Update Snapmaker U1 printer profile and G-code sequences (#12219) * Sync Snapmaker U1 profiles with upstream v2.2.1 * Customize Snapmaker U1 start gcode: auto bed leveling and adaptive mesh * fix bed mesh min/max regression issues --------- Co-authored-by: SoftFever --- ...er U1_texture.svg => Snapmaker U1_bed.svg} | 0 .../machine/Snapmaker U1 (0.4 nozzle).json | 26 +++++++++++-------- .../profiles/Snapmaker/machine/fdm_U1.json | 11 ++++---- 3 files changed, 21 insertions(+), 16 deletions(-) rename resources/profiles/Snapmaker/{Snapmaker U1_texture.svg => Snapmaker U1_bed.svg} (100%) diff --git a/resources/profiles/Snapmaker/Snapmaker U1_texture.svg b/resources/profiles/Snapmaker/Snapmaker U1_bed.svg similarity index 100% rename from resources/profiles/Snapmaker/Snapmaker U1_texture.svg rename to resources/profiles/Snapmaker/Snapmaker U1_bed.svg diff --git a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 nozzle).json b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 nozzle).json index 3c7d6fa7aa..6422735955 100644 --- a/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 nozzle).json +++ b/resources/profiles/Snapmaker/machine/Snapmaker U1 (0.4 nozzle).json @@ -1,14 +1,14 @@ { "type": "machine", + "setting_id": "SM_U1", "name": "Snapmaker U1 (0.4 nozzle)", - "inherits": "fdm_U1", "from": "system", - "setting_id": "1591507869", "instantiation": "true", + "inherits": "fdm_U1", "printer_model": "Snapmaker U1", "printer_variant": "0.4", "auxiliary_fan": "1", - "change_filament_gcode": "; Change Tool[previous_extruder] -> Tool[next_extruder] (layer [layer_num])\n{\nlocal max_speed_toolchange = 350.0;\nlocal wait_for_extruder_temp = true;\nposition[2] = position[2] + 2.0;\n\nlocal speed_toolchange = max_speed_toolchange;\nif travel_speed < max_speed_toolchange then\n speed_toolchange = travel_speed;\nendif\n\"G91\nG0 Z1.5 F600\nG90\n\";\n\"G1 F\" + (speed_toolchange * 60) + \"\n\";\nif wait_for_extruder_temp and not((layer_num < 0) and (next_extruder == initial_tool)) then\n \"\n\";\n \"; \" + layer_num + \"\n\";\n if layer_num == 0 then\n \"M109 S\" + first_layer_temperature[next_extruder] + \" T\" + next_extruder + \"\n\";\n else\n \"M109 S\" + temperature[next_extruder] + \" T\" + next_extruder + \"\n\";\n endif\nendif\n\"T\" + next_extruder + \"\n\";\n}\nM400\n{if filament_type[next_extruder] == \"PVA\"}\nSET_VELOCITY_LIMIT ACCEL=3000\n{else}\n{endif}", + "change_filament_gcode": ";===== date: 20251213=====================\n; Change Tool[previous_extruder] -> Tool[next_extruder] (layer [layer_num])\n{\nlocal max_speed_toolchange = 350.0;\nlocal wait_for_extruder_temp = true;\nposition[2] = position[2] + 2.0;\nlocal speed_toolchange = max_speed_toolchange;\nif travel_speed < max_speed_toolchange then\n speed_toolchange = travel_speed;\nendif\n\"G91\nG1 Z1.5 F1800\nG90\n\";\n\"G1 F\" + (speed_toolchange * 60) + \"\n\";\nif wait_for_extruder_temp and not((layer_num < 0) and (next_extruder == initial_tool)) then\n \"\n\";\n \"; \" + layer_num + \"\n\";\n if layer_num == 0 then\n \"M109 S\" + first_layer_temperature[next_extruder] + \" T\" + next_extruder + \"\n\";\n else\n \"M109 S\" + temperature[next_extruder] + \" T\" + next_extruder + \"\n\";\n endif\nendif\n\"M400\" + \"\n\";\n\"T\" + next_extruder + \"\n\";\nif filament_type[next_extruder] == \"PVA\" then\n\"SET_VELOCITY_LIMIT ACCEL=3000\n\";\nelse\nendif\nif previous_extruder != next_extruder and initial_extruder != next_extruder then\n\"SM_PRINT_PREEXTRUDE_FILAMENT INDEX=\" + next_extruder + \"\n\";\nendif\n\"G90\n\";\n}\n", "extruder_colour": [ "#FCE94F", "#FCE94F", @@ -41,6 +41,7 @@ "20", "12" ], + "machine_start_gcode": "SET_PRINT_AUTO_BED_LEVELING ENABLE=1\nSET_TIME_LAPSE_CAMERA ENABLE=1\n;===== date: 20251222 =====================\n\nPRINT_START\nDEFECT_DETECTION_START\nSET_PRINT_STATS_INFO TOTAL_LAYER={total_layer_count}\nSET_PRINT_STATS_INFO CURRENT_LAYER=0\nTIMELAPSE_START\nM140 S{bed_temperature_initial_layer_single}\nM104 T{initial_extruder} S140\nM204 S10000\n\nG28 X Y\n;===== 床面异物检测 ========\nT{initial_extruder}\nG90\nDEFECT_DETECTION_DETECT_BED\n;===== 取放头检测 =================\nSM_PRINT_CHECK_SWITCH_EXTRUDER\n\n;===== 自动进料 & 挤出流量 & 预挤出 ======================\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=1 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=0\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=0\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=2 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=1\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=1\nSM_PRINT_EXTRUDER_PREHEAT EXTRUDER=3 TEMP=140\nSM_PRINT_AUTO_FEED EXTRUDER=2\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=2\nSM_PRINT_AUTO_FEED EXTRUDER=3\nSM_PRINT_FLOW_CALIBRATE EXTRUDER=3\nM104 S0 T0 A0\nM104 S0 T1 A0\nM104 S0 T2 A0\nM104 S0 T3 A0\nM104 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\n\n;===== 粗回零 =================\nT{initial_extruder}\nM106 S255\nM106 P2 S0\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 T{initial_extruder} S{nozzle_temperature[initial_extruder] - 90}\nROUGHLY_CLEAN_NOZZLE_WITH_DISCARD\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nG28 Z I140 J140\n\n;===== 检测钢板 =================\nDETECT_BED_PLATE\n\n;===== 深度清洁喷嘴 =================\nG90\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 S{nozzle_temperature[initial_extruder] - 50}\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_1\nM104 S{nozzle_temperature[initial_extruder] - 90}\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\nFINELY_CLEAN_NOZZLE_STAGE_2\n\n;===== 精回零 =================\nM106 S255\nM109 S{nozzle_temperature[initial_extruder] - 90}\nM190 S{bed_temperature_initial_layer_single}\nM107 P2\nG90\nG0 Z5 F10000\nG28 Z\n\n;===== 热床调平 =================\n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0\n; Original upstream: BED_MESH_CALIBRATE PROBE_COUNT=11,11\n\n;===== 画起始线 =================\nG90\nG1 Z1.5\nG0 X10 Y3 Z2 F18000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG1 Z0.2\nM83\nG1 X110 E15 F360\nG1 Z1.5\n\nG90\nM106 S0", "machine_tool_change_time": "5", "max_layer_height": [ "0.32", @@ -60,14 +61,14 @@ "0.4", "0.4" ], - "nozzle_type": "hardened_steel", + "nozzle_type": "stainless_steel", "printable_area": [ - "-0.5x-1", - "270.5x-1", + "0.5x1", + "270.5x1", "270.5x271", - "-0.5x271" + "0.5x271" ], - "printable_height": "270", + "printable_height": "270.05", "printer_settings_id": "MyToolChanger 0.4 nozzle - Copy", "retract_before_wipe": [ "0%", @@ -183,7 +184,7 @@ "extruder_clearance_radius": "72.5", "machine_load_filament_time": "0", "machine_unload_filament_time": "0", - "before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]\nG92 E0\nTIMELAPSE_TAKE_FRAME", + "before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]\nG92 E0\nTIMELAPSE_TAKE_FRAME\nDEFECT_DETECTION_DETECT", "z_hop_when_prime": [ "0", "0", @@ -192,6 +193,9 @@ ], "ramming_pressure_advance_value": "0.02", "tool_change_temprature_wait": "0", - "printer_notes": "1、修改幅面坐标,原点坐标\n2、修改换头时间,5S", - "machine_pause_gcode": "M600" + "printer_notes": "", + "machine_pause_gcode": "M600", + "default_bed_type": "Textured PEI Plate", + "layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]\nSET_PRINT_STATS_INFO TOTAL_LAYER={total_layer_count}\nSET_PRINT_STATS_INFO CURRENT_LAYER={layer_num+1}", + "nozzle_volume": "143" } \ No newline at end of file diff --git a/resources/profiles/Snapmaker/machine/fdm_U1.json b/resources/profiles/Snapmaker/machine/fdm_U1.json index fca1592792..ab6170bf60 100644 --- a/resources/profiles/Snapmaker/machine/fdm_U1.json +++ b/resources/profiles/Snapmaker/machine/fdm_U1.json @@ -7,7 +7,7 @@ "gcode_flavor": "klipper", "single_extruder_multi_material": "0", "bed_model": "Snapmaker U1_bed.stl", - "bed_texture": "Snapmaker U1_texture.svg", + "bed_texture": "Snapmaker U1_bed.svg", "max_layer_height": [ "0.32", "0.32", @@ -180,9 +180,10 @@ "bed_mesh_min": "3,3", "purge_in_prime_tower": "0", "machine_pause_gcode": "M601", - "change_filament_gcode": "", - "machine_start_gcode": ";===== machine: PR2 ========================\n;===== date: 20250717 =====================\nPRINT_START\n;===== 预热热床和第一个挤出头 =================\nM140 S{bed_temperature_initial_layer_single}\nM104 T{initial_extruder} S140\n\n;===== 粗回零 =================\nG28 X Y\nT{initial_extruder}\nM109 T{initial_extruder} S140\nG28 Z\nG90\nG0 Z10 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nM109 S{nozzle_temperature[initial_extruder] - 50}\nROUGHLY_CLEAN_NOZZLE_WITH_DISCARD\nM104 S{nozzle_temperature[initial_extruder] - 90}\nG4 P2000\nROUGHLY_CLEAN_NOZZLE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\n\n;===== 检测钢板 =================\nDETECT_BED_PLATE\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\n\n;===== 自动进料 ======================\nSM_PRINT_AUTO_FEED EXTRUDER=0\nSM_PRINT_AUTO_FEED EXTRUDER=1\nSM_PRINT_AUTO_FEED EXTRUDER=2\nSM_PRINT_AUTO_FEED EXTRUDER=3\n\n;===== 挤出流量 ======================\n{if (is_extruder_used[0])}\nSM_PRINT_FLOW_CALIBRATE INDEX=0 TARGET_TEMP={nozzle_temperature[0]}\n{endif}\n{if (is_extruder_used[1])}\nSM_PRINT_FLOW_CALIBRATE INDEX=1 TARGET_TEMP={nozzle_temperature[1]}\n{endif}\n{if (is_extruder_used[2])}\nSM_PRINT_FLOW_CALIBRATE INDEX=2 TARGET_TEMP={nozzle_temperature[2]}\n{endif}\n{if (is_extruder_used[3])}\nSM_PRINT_FLOW_CALIBRATE INDEX=3 TARGET_TEMP={nozzle_temperature[3]}\n{endif}\n\n;===== 取出第一个挤出头 =================\nT{initial_extruder}\nSET_VELOCITY_LIMIT ACCEL=10000\nM204 S10000\n\n;===== 深度清洁喷嘴 =================\nG90\nG0 Z10 F10000\nROUGHLY_CLEAN_NOZZLE_WITH_DISCARD\nG0 Z5 F10000\nFINELY_CLEAN_NOZZLE_STAGE_1\nG0 Z5 F10000\nROUGHLY_CLEAN_NOZZLE\nG0 Z5 F10000\nFINELY_CLEAN_NOZZLE_STAGE_2\nM83\n\n;===== 第一个挤出头降温 =================\nM109 S{nozzle_temperature[initial_extruder] - 90}\nM190 S{bed_temperature_initial_layer_single}\nM106 S0\nG90\nG0 Z5 F10000\nMOVE_TO_DISCARD_FILAMENT_POSITION\nINNER_CUTOFF_BASE_DISCARD\nINNER_ROUGHLY_CLEAN_NOZZLE_BASE_DISCARD\nINNER_ROUGHLY_CLEAN_NOZZLE_BASE_DISCARD\nMOVE_TO_XY_IDLE_POSITION_EXTRUDER\n\n;===== 精回零 =================\nG28 Z\n;===== 热床调平 =================\n; Always pass `ADAPTIVE_MARGIN=0` because Orca has already handled `adaptive_bed_mesh_margin` internally\n; Make sure to set ADAPTIVE to 0 otherwise Klipper will use it's own adaptive bed mesh logic\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0\n\n;BED_MESH_CALIBRATE PROBE_COUNT=7,7\n\n\n;======== 预挤出/划线 ================\n{if (is_extruder_used[0]) and 0 != initial_extruder}\nSM_PRINT_START_LINE INDEX=0 TARGET_TEMP={nozzle_temperature_initial_layer[0]}\nM83\nM104 S{nozzle_temperature[0] - 90}\n{endif}\n\n{if (is_extruder_used[1]) and 1 != initial_extruder}\nSM_PRINT_START_LINE INDEX=1 TARGET_TEMP={nozzle_temperature_initial_layer[1]}\nM83\nM104 S{nozzle_temperature[1] - 90}\n{endif}\n\n{if (is_extruder_used[2]) and 2 != initial_extruder}\nSM_PRINT_START_LINE INDEX=2 TARGET_TEMP={nozzle_temperature_initial_layer[2]}\nM83\nM104 S{nozzle_temperature[2] - 90}\n{endif}\n\n{if (is_extruder_used[3]) and 3 != initial_extruder}\nSM_PRINT_START_LINE INDEX=3 TARGET_TEMP={nozzle_temperature_initial_layer[3]}\nM83\nM104 S{nozzle_temperature[3] - 90}\n{endif}\n\n{if (is_extruder_used[initial_extruder])}\nSM_PRINT_START_LINE INDEX={initial_extruder} TARGET_TEMP={nozzle_temperature_initial_layer[initial_extruder]}\n{endif}\nM109 S{nozzle_temperature_initial_layer[initial_extruder]} T{initial_extruder}\nM106 S0\n\nTIMELAPSE_START", + "change_filament_gcode": "", + "machine_start_gcode": "PRINT_START TOOL_TEMP={first_layer_temperature[initial_tool]} {if is_extruder_used[0]}T0_TEMP={first_layer_temperature[0]}{endif} {if is_extruder_used[1]}T1_TEMP={first_layer_temperature[1]}{endif} {if is_extruder_used[2]}T2_TEMP={first_layer_temperature[2]}{endif} {if is_extruder_used[3]}T3_TEMP={first_layer_temperature[3]}{endif} {if is_extruder_used[4]}T4_TEMP={first_layer_temperature[4]}{endif} {if is_extruder_used[5]}T5_TEMP={first_layer_temperature[5]}{endif} BED_TEMP=[first_layer_bed_temperature] TOOL=[initial_tool]\n\nM83\n; set extruder temp\n{if first_layer_temperature[0] > 0 and (is_extruder_used[0])}M104 T0 S{first_layer_temperature[0]}{endif}\n{if first_layer_temperature[1] > 0 and (is_extruder_used[1])}M104 T1 S{first_layer_temperature[1]}{endif}\n{if first_layer_temperature[2] > 0 and (is_extruder_used[2])}M104 T2 S{first_layer_temperature[2]}{endif}\n{if first_layer_temperature[3] > 0 and (is_extruder_used[3])}M104 T3 S{first_layer_temperature[3]}{endif}\n{if first_layer_temperature[4] > 0 and (is_extruder_used[4])}M104 T4 S{first_layer_temperature[4]}{endif}\n{if (is_extruder_used[0]) and initial_tool != 0}\n;\n; purge first tool\n;\nG1 F{travel_speed * 60}\nM109 T0 S{first_layer_temperature[0]}\nT0; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(0 == 0 ? 0 : (0 == 1 ? 120 : (0 == 2 ? 180 : 300)))} Y{(0 < 4 ? 0 : 3)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if filament_multitool_ramming[0]}10{else}30{endif} X40 Z0.2 F{if filament_multitool_ramming[0]}500{else}170{endif} ; purge while moving towards the sheet\nG0 X40 E9 F800 ; continue purging and wipe the nozzle\nG0 X{40 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{40 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[0]} F2400 ; retract\n{e_retracted[0] = 1.5 * retract_length[0]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(idle_temperature[0] == 0 ? (first_layer_temperature[0] + standby_temperature_delta) : (idle_temperature[0]))} T0\n{endif}\n{if (is_extruder_used[1]) and initial_tool != 1}\n;\n; purge second tool\n;\nG1 F{travel_speed * 60}\nM109 T1 S{first_layer_temperature[1]}\nT1; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(1 == 0 ? 0 : (1 == 1 ? 120 : (1 == 2 ? 180 : 300)))} Y{(1 < 4 ? 0 : 3)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if filament_multitool_ramming[1]}10{else}30{endif} X120 Z0.2 F{if filament_multitool_ramming[1]}500{else}170{endif} ; purge while moving towards the sheet\nG0 X80 E9 F800 ; continue purging and wipe the nozzle\nG0 X{80 - 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{80 - 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[1]} F2400 ; retract\n{e_retracted[1] = 1.5 * retract_length[1]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(idle_temperature[1] == 0 ? (first_layer_temperature[1] + standby_temperature_delta) : (idle_temperature[1]))} T1\n{endif}\n{if (is_extruder_used[2]) and initial_tool != 2}\n;\n; purge third tool\n;\nG1 F{travel_speed * 60}\nM109 T2 S{first_layer_temperature[2]}\nT2; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(2 == 0 ? 0 : (2 == 1 ? 120 : (2 == 2 ? 180 : 300)))} Y{(2 < 4 ? 0 : 3)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if filament_multitool_ramming[2]}10{else}30{endif} X220 Z0.2 F{if filament_multitool_ramming[2]}500{else}170{endif} ; purge while moving towards the sheet\nG0 X220 E9 F800 ; continue purging and wipe the nozzle\nG0 X{220 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{220 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[2]} F2400 ; retract\n{e_retracted[2] = 1.5 * retract_length[2]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(idle_temperature[2] == 0 ? (first_layer_temperature[2] + standby_temperature_delta) : (idle_temperature[2]))} T2\n{endif}\n{if (is_extruder_used[3]) and initial_tool != 3}\n;\n; purge fourth tool\n;\nG1 F{travel_speed * 60}\nM109 T3 S{first_layer_temperature[3]}\nT3; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(3 == 0 ? 0 : (3 == 1 ? 120 : (3 == 2 ? 180 : 300)))} Y{(3 < 4 ? 0 : 3)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if filament_multitool_ramming[3]}10{else}30{endif} X290 Z0.2 F{if filament_multitool_ramming[3]}500{else}170{endif} ; purge while moving towards the sheet\nG0 X260 E9 F800 ; continue purging and wipe the nozzle\nG0 X{260 - 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{260 - 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[3]} F2400 ; retract\n{e_retracted[3] = 1.5 * retract_length[3]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(idle_temperature[3] == 0 ? (first_layer_temperature[3] + standby_temperature_delta) : (idle_temperature[3]))} T3\n{endif}\n{if (is_extruder_used[4]) and initial_tool != 4}\n;\n; purge fifth tool\n;\nG1 F{travel_speed * 60}\nM109 T4 S{first_layer_temperature[4]}\nT4; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(4 == 0 ? 0 : (4 == 1 ? 120 : (4 == 2 ? 180 : 300)))} Y{(4 < 4 ? 0 : 3)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if filament_multitool_ramming[4]}10{else}30{endif} X290 Z0.2 F{if filament_multitool_ramming[4]}500{else}170{endif} ; purge while moving towards the sheet\nG0 X260 E9 F800 ; continue purging and wipe the nozzle\nG0 X{260 - 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{260 - 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[4]} F2400 ; retract\n{e_retracted[4] = 1.5 * retract_length[4]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(idle_temperature[4] == 0 ? (first_layer_temperature[4] + standby_temperature_delta) : (idle_temperature[4]))} T4\n{endif}\n;\n; purge initial tool\n;\nG1 F{travel_speed * 60}\nM109 T{initial_tool} S{first_layer_temperature[initial_tool]}\nT{initial_tool}; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(initial_tool == 0 ? 0 : (initial_tool == 1 ? 120 : (initial_tool == 2 ? 180 : 300)))} Y{(initial_tool < 4 ? 0 : 3)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if filament_multitool_ramming[initial_tool]}10{else}30{endif} X{(initial_tool == 0 ? 0 : (initial_tool == 1 ? 120 : (initial_tool == 2 ? 180 : 300))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 10)} Z0.2 F{if filament_multitool_ramming[initial_tool]}500{else}170{endif} ; purge while moving towards the sheet\nG0 X{(initial_tool == 0 ? 0 : (initial_tool == 1 ? 120 : (initial_tool == 2 ? 180 : 300))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 40)} E9 F800 ; continue purging and wipe the nozzle\nG0 X{(initial_tool == 0 ? 0 : (initial_tool == 1 ? 120 : (initial_tool == 2 ? 180 : 300))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 40) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 3)} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{(initial_tool == 0 ? 0 : (initial_tool == 1 ? 120 : (initial_tool == 2 ? 180 : 300))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 40) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 3 * 2)} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[initial_tool]} F2400 ; retract\n{e_retracted[initial_tool] = 1.5 * retract_length[initial_tool]}\nG92 E0 ; reset extruder position\n", "scan_first_layer": "0", - "nozzle_type": "hardened_steel", - "auxiliary_fan": "0" + "nozzle_type": "undefine", + "auxiliary_fan": "0", + "default_bed_type": "Textured PEI Plate" } \ No newline at end of file From ed3f0e28983a39a79154c2f762de419d20bbf64b Mon Sep 17 00:00:00 2001 From: Argo <52103738+Argolein@users.noreply.github.com> Date: Tue, 10 Feb 2026 03:56:38 +0100 Subject: [PATCH 3/5] Fix issue #10971 (#12108) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sorry had to re-create the PR as I did some chaos in my repo with CLI. xD Fixes issue https://github.com/OrcaSlicer/OrcaSlicer/issues/10971 Description: Fix wipe tower filament selection and clean up tool ordering. Added wipe_tower_filament handling to WipeTower2 (store config, mark non-selected tools as “soluble,” and use it in toolchange selection) and ensured the configured wipe‑tower extruder is included in the extruder list for ordering. Removed duplicated/merged tool‑ordering code (extra insert_wipe_tower_extruder definition, duplicate declaration, and redundant reorder block) so the tool order logic runs only once. image --- src/libslic3r/GCode/ToolOrdering.cpp | 36 ++++++++++++++++++++++++---- src/libslic3r/GCode/ToolOrdering.hpp | 1 + src/libslic3r/GCode/WipeTower.cpp | 2 +- src/libslic3r/GCode/WipeTower2.cpp | 22 ++++++++++------- src/libslic3r/GCode/WipeTower2.hpp | 1 + src/libslic3r/Print.cpp | 6 +++++ 6 files changed, 55 insertions(+), 13 deletions(-) diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 6de65a5dd6..08e004bc30 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -306,6 +306,26 @@ void ToolOrdering::handle_dontcare_extruder(unsigned int last_extruder_id) } } +bool ToolOrdering::insert_wipe_tower_extruder() +{ + if (!m_print_config_ptr || !m_print_config_ptr->enable_prime_tower) + return false; + if (m_print_config_ptr->wipe_tower_filament == 0) + return false; + + bool changed = false; + const unsigned int wipe_extruder = (unsigned int)(m_print_config_ptr->wipe_tower_filament - 1); + for (LayerTools < : m_layer_tools) { + if (lt.wipe_tower_partitions > 0) { + if (std::find(lt.extruders.begin(), lt.extruders.end(), wipe_extruder) == lt.extruders.end()) { + lt.extruders.emplace_back(wipe_extruder); + changed = true; + } + } + } + return changed; +} + void ToolOrdering::sort_and_build_data(const Print& print, unsigned int first_extruder, bool prime_multi_material) { // if first extruder is -1, we can decide the first layer tool order before doing reorder function @@ -328,9 +348,13 @@ void ToolOrdering::sort_and_build_data(const Print& print, unsigned int first_ex max_layer_height = calc_max_layer_height(print.config(), max_layer_height); - this->collect_extruder_statistics(prime_multi_material); - this->fill_wipe_tower_partitions(print.config(), object_bottom_z, max_layer_height); + if (this->insert_wipe_tower_extruder()) { + reorder_extruders_for_minimum_flush_volume(reorder_first_layer); + this->fill_wipe_tower_partitions(print.config(), object_bottom_z, max_layer_height); + } + + this->collect_extruder_statistics(prime_multi_material); } void ToolOrdering::sort_and_build_data(const PrintObject& object , unsigned int first_extruder, bool prime_multi_material) @@ -343,9 +367,13 @@ void ToolOrdering::sort_and_build_data(const PrintObject& object , unsigned int double max_layer_height = calc_max_layer_height(object.print()->config(), object.config().layer_height); - this->collect_extruder_statistics(prime_multi_material); - this->fill_wipe_tower_partitions(object.print()->config(), object.layers().front()->print_z - object.layers().front()->height, max_layer_height); + if (this->insert_wipe_tower_extruder()) { + reorder_extruders_for_minimum_flush_volume(reorder_first_layer); + this->fill_wipe_tower_partitions(object.print()->config(), object.layers().front()->print_z - object.layers().front()->height, max_layer_height); + } + + this->collect_extruder_statistics(prime_multi_material); } diff --git a/src/libslic3r/GCode/ToolOrdering.hpp b/src/libslic3r/GCode/ToolOrdering.hpp index 7f7fc6a6ce..7bbfa2da1f 100644 --- a/src/libslic3r/GCode/ToolOrdering.hpp +++ b/src/libslic3r/GCode/ToolOrdering.hpp @@ -258,6 +258,7 @@ private: void initialize_layers(std::vector &zs); void collect_extruders(const PrintObject &object, const std::vector> &per_layer_extruder_switches); void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z, coordf_t max_layer_height); + bool insert_wipe_tower_extruder(); void mark_skirt_layers(const PrintConfig &config, coordf_t max_layer_height); void collect_extruder_statistics(bool prime_multi_material); void reorder_extruders_for_minimum_flush_volume(bool reorder_first_layer); diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index 0be6748594..4c6ed404b5 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -1540,7 +1540,7 @@ void WipeTower::set_extruder(size_t idx, const PrintConfig& config) m_filpar.push_back(FilamentParameters()); m_filpar[idx].material = config.filament_type.get_at(idx); - m_filpar[idx].is_soluble = config.filament_soluble.get_at(idx); + m_filpar[idx].is_soluble = config.wipe_tower_filament == 0 ? config.filament_soluble.get_at(idx) : (idx != size_t(config.wipe_tower_filament - 1)); // BBS m_filpar[idx].is_support = config.filament_is_support.get_at(idx); m_filpar[idx].nozzle_temperature = config.nozzle_temperature.get_at(idx); diff --git a/src/libslic3r/GCode/WipeTower2.cpp b/src/libslic3r/GCode/WipeTower2.cpp index 19e98e4a09..ba22338eba 100644 --- a/src/libslic3r/GCode/WipeTower2.cpp +++ b/src/libslic3r/GCode/WipeTower2.cpp @@ -1227,6 +1227,7 @@ WipeTower::ToolChangeResult WipeTower2::construct_tcr(WipeTowerWriter2& writer, WipeTower2::WipeTower2(const PrintConfig& config, const PrintRegionConfig& default_region_config,int plate_idx, Vec3d plate_origin, const std::vector>& wiping_matrix, size_t initial_tool) : m_semm(config.single_extruder_multi_material.value), m_enable_filament_ramming(config.enable_filament_ramming.value), + m_wipe_tower_filament(config.wipe_tower_filament.value), m_wipe_tower_pos(config.wipe_tower_x.get_at(plate_idx), config.wipe_tower_y.get_at(plate_idx)), m_wipe_tower_width(float(config.prime_tower_width)), m_wipe_tower_rotation_angle(float(config.wipe_tower_rotation_angle)), @@ -1309,7 +1310,10 @@ void WipeTower2::set_extruder(size_t idx, const PrintConfig& config) m_filpar.push_back(FilamentParameters()); m_filpar[idx].material = config.filament_type.get_at(idx); - m_filpar[idx].is_soluble = config.filament_soluble.get_at(idx); + if (m_wipe_tower_filament > 0) + m_filpar[idx].is_soluble = (idx != size_t(m_wipe_tower_filament - 1)); + else + m_filpar[idx].is_soluble = config.filament_soluble.get_at(idx); m_filpar[idx].temperature = config.nozzle_temperature.get_at(idx); m_filpar[idx].first_layer_temperature = config.nozzle_temperature_initial_layer.get_at(idx); m_filpar[idx].filament_minimal_purge_on_wipe_tower = config.filament_minimal_purge_on_wipe_tower.get_at(idx); @@ -2224,14 +2228,16 @@ void WipeTower2::save_on_last_wipe() int WipeTower2::first_toolchange_to_nonsoluble( const std::vector& tool_changes) const { - // Orca: allow calculation of the required depth and wipe volume for soluable toolchanges as well - // NOTE: it's not clear if this is the right way, technically we should disable wipe tower if soluble filament is used as it - // will will make the wipe tower unstable. Need to revist this in the future. + // If a specific wipe tower filament is forced, use it to decide where to finish the layer. + if (m_wipe_tower_filament > 0) { + for (size_t idx = 0; idx < tool_changes.size(); ++idx) { + if (!m_filpar[tool_changes[idx].new_tool].is_soluble) + return idx; + } + return -1; + } + // Orca: allow calculation of the required depth and wipe volume for soluble toolchanges as well. return tool_changes.empty() ? -1 : 0; - //for (size_t idx=0; idx Print::extruders(bool conside_custom_gcode) const } } + // If a wipe tower filament is explicitly set, ensure it participates in tool ordering. + if (has_wipe_tower() && config().wipe_tower_filament != 0 && extruders.size() > 1) { + assert(config().wipe_tower_filament > 0 && config().wipe_tower_filament < int(config().nozzle_diameter.size())); + extruders.emplace_back(config().wipe_tower_filament - 1); // config value is 1-based + } + sort_remove_duplicates(extruders); return extruders; } From abefb0e698d1af82e830b3e695376d34e36853d1 Mon Sep 17 00:00:00 2001 From: Ocraftyone Date: Tue, 10 Feb 2026 00:08:22 -0500 Subject: [PATCH 4/5] Fix thumbnail rendering in CLI (#12129) * Fix thumbnail rendering in CLI * Remove OSMesa context flag * Add stringified error message for glfwInit and glewInit * micro refactor --------- Co-authored-by: SoftFever --- src/OrcaSlicer.cpp | 25 +++++++++++-------------- src/slic3r/GUI/3DScene.cpp | 12 ++++++------ src/slic3r/GUI/Camera.cpp | 6 ++++-- src/slic3r/GUI/GLModel.cpp | 10 ++++++---- src/slic3r/GUI/GLModel.hpp | 6 +++--- src/slic3r/GUI/OpenGLManager.cpp | 2 +- src/slic3r/GUI/PartPlate.cpp | 7 +++++-- 7 files changed, 36 insertions(+), 32 deletions(-) diff --git a/src/OrcaSlicer.cpp b/src/OrcaSlicer.cpp index a3ab6450d8..397cd66d38 100644 --- a/src/OrcaSlicer.cpp +++ b/src/OrcaSlicer.cpp @@ -1316,14 +1316,14 @@ int CLI::run(int argc, char **argv) return (argc == 0) ? 0 : 1; #endif // SLIC3R_GUI } + + // Setup logging for CLI + const ConfigOptionInt* opt_loglevel = m_config.opt("debug"); + if (opt_loglevel) { + set_logging_level(opt_loglevel->value); + } else { - const ConfigOptionInt *opt_loglevel = m_config.opt("debug"); - if (opt_loglevel) { - set_logging_level(opt_loglevel->value); - } - else { - set_logging_level(2); - } + set_logging_level(2); } global_begin_time = (long long)Slic3r::Utils::get_current_time_utc(); @@ -6401,8 +6401,9 @@ int CLI::run(int argc, char **argv) glfwSetErrorCallback(glfw_callback); int ret = glfwInit(); if (ret == GLFW_FALSE) { - int code = glfwGetError(NULL); - BOOST_LOG_TRIVIAL(error) << "glfwInit return error, code " <(0, -1)) - model.render(); + model.render(shader); else - model.render(this->tverts_range); + model.render(this->tverts_range, shader); glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); // 2nd. render pass, just a normal render with the depth buffer passed as a texture @@ -576,15 +576,15 @@ void GLVolume::simple_render(GLShaderProgram* shader, ModelObjectPtrs& model_obj } } if (tverts_range == std::make_pair(0, -1)) - m.render(); + m.render(shader); else - m.render(this->tverts_range); + m.render(this->tverts_range, shader); } } else { if (tverts_range == std::make_pair(0, -1)) - model.render(); + model.render(shader); else - model.render(this->tverts_range); + model.render(this->tverts_range, shader); } if (this->is_left_handed()) glFrontFace(GL_CCW); diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index efd9bad0b0..3bde13f91d 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -55,7 +55,10 @@ void Camera::select_next_type() void Camera::auto_type(EType preferred_type) { - if (!wxGetApp().app_config->get_bool("auto_perspective")) return; + if (wxApp::GetInstance() == nullptr || wxGetApp().app_config == nullptr) + return; + if (!wxGetApp().app_config->get_bool("auto_perspective")) + return; if (preferred_type == EType::Perspective) { if (!m_prevent_auto_type) { set_type(preferred_type); @@ -758,4 +761,3 @@ void Camera::update_target() { } } // GUI } // Slic3r - diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index 507768ad64..523db38c25 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -596,12 +596,12 @@ static GLenum get_index_type(const GLModel::Geometry& data) } } -void GLModel::render() +void GLModel::render(GLShaderProgram* shader) { - render(std::make_pair(0, indices_count())); + render(std::make_pair(0, indices_count()), shader); } -void GLModel::render(const std::pair& range) +void GLModel::render(const std::pair& range, GLShaderProgram* shader) { if (m_render_disabled) return; @@ -609,7 +609,9 @@ void GLModel::render(const std::pair& range) if (range.second == range.first) return; - GLShaderProgram* shader = wxGetApp().get_current_shader(); + if (shader == nullptr && wxApp::GetInstance() != nullptr) + shader = wxGetApp().get_current_shader(); + if (shader == nullptr) return; diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index 13729f5a9e..d007b31371 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -13,6 +13,7 @@ namespace Slic3r { class TriangleMesh; class Polygon; +class GLShaderProgram; using Polygons = std::vector>; class BuildVolume; @@ -177,8 +178,8 @@ namespace GUI { const ColorRGBA& get_color() const { return m_render_data.geometry.color; } void reset(); - void render(); - void render(const std::pair& range); + void render(GLShaderProgram* shader = nullptr); + void render(const std::pair& range, GLShaderProgram* shader = nullptr); void render_instanced(unsigned int instances_vbo, unsigned int instances_count); bool is_initialized() const { return vertices_count() > 0 && indices_count() > 0; } @@ -260,4 +261,3 @@ namespace GUI { } // namespace Slic3r #endif // slic3r_GLModel_hpp_ - diff --git a/src/slic3r/GUI/OpenGLManager.cpp b/src/slic3r/GUI/OpenGLManager.cpp index 3bbc8927b6..39860ab9a8 100644 --- a/src/slic3r/GUI/OpenGLManager.cpp +++ b/src/slic3r/GUI/OpenGLManager.cpp @@ -253,7 +253,7 @@ bool OpenGLManager::init_gl(bool popup_error) glewExperimental = true; GLenum result = glewInit(); if (result != GLEW_OK) { - BOOST_LOG_TRIVIAL(error) << "Unable to init glew library"; + BOOST_LOG_TRIVIAL(error) << "Unable to init glew library, Error: " << glewGetErrorString(result); return false; } //BOOST_LOG_TRIVIAL(info) << "glewInit Success."<< std::endl; diff --git a/src/slic3r/GUI/PartPlate.cpp b/src/slic3r/GUI/PartPlate.cpp index acb8240523..33efc32507 100644 --- a/src/slic3r/GUI/PartPlate.cpp +++ b/src/slic3r/GUI/PartPlate.cpp @@ -3110,8 +3110,11 @@ bool PartPlate::set_shape(const Pointfs& shape, const Pointfs& exclude_areas, co calc_vertex_for_icons(3, m_lock_icon); calc_vertex_for_icons(4, m_plate_settings_icon); // ORCA also change bed_icon_count number in calc_vertex_for_icons() after adding or removing icons for circular shaped beds that uses vertical alingment for icons - PresetBundle* preset = wxGetApp().preset_bundle; - bool dual_bbl = (preset->is_bbl_vendor() && preset->get_printer_extruder_count() == 2); + bool dual_bbl = false; + if (m_plater) { + PresetBundle* preset = wxGetApp().preset_bundle; + dual_bbl = (preset->is_bbl_vendor() && preset->get_printer_extruder_count() == 2); + } calc_vertex_for_icons(dual_bbl ? 5 : 6, m_plate_filament_map_icon); calc_vertex_for_icons(dual_bbl ? 6 : 5, m_move_front_icon); From 38e67ff090a4ba0caec30acd8bb45ae0a3f329d8 Mon Sep 17 00:00:00 2001 From: Ocraftyone Date: Tue, 10 Feb 2026 00:53:11 -0500 Subject: [PATCH 5/5] Improve the look of new version release notes (#12229) # Description Adds md4c to parse release notes to HTML # Screenshots/Recordings/Graphs image ## Tests Tested on Windows --- src/slic3r/CMakeLists.txt | 2 +- src/slic3r/GUI/GUI_App.cpp | 2 +- src/slic3r/GUI/ReleaseNote.cpp | 84 ++++++++++++++++++---------------- src/slic3r/GUI/ReleaseNote.hpp | 1 + 4 files changed, 47 insertions(+), 42 deletions(-) diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 52ba10ab03..7ec3215940 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -736,7 +736,7 @@ else() set(_opengl_link_lib OpenGL::GL) endif() -target_link_libraries(libslic3r_gui libslic3r cereal::cereal imgui imguizmo minilzo libvgcode GLEW::GLEW OpenGL::GL hidapi ${wxWidgets_LIBRARIES} glfw libcurl OpenSSL::SSL OpenSSL::Crypto noise::noise) +target_link_libraries(libslic3r_gui libslic3r cereal::cereal imgui imguizmo minilzo libvgcode md4c-html GLEW::GLEW OpenGL::GL hidapi ${wxWidgets_LIBRARIES} glfw libcurl OpenSSL::SSL OpenSSL::Crypto noise::noise) if (MSVC) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 6c8dd5f421..5a39ea4647 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -5423,7 +5423,7 @@ void GUI_App::check_new_version_sf(bool show_tips, int by_user) } version_info.url = prefer_release ? best_release_url : best_pre_url; - version_info.version_str = prefer_release ? best_release.to_string_sf() : best_pre.to_string(); + version_info.version_str = prefer_release ? best_release.to_string_sf() : best_pre.to_string_sf(); version_info.description = prefer_release ? best_release_content : best_pre_content; version_info.force_upgrade = false; diff --git a/src/slic3r/GUI/ReleaseNote.cpp b/src/slic3r/GUI/ReleaseNote.cpp index 4af803d17d..20126bf254 100644 --- a/src/slic3r/GUI/ReleaseNote.cpp +++ b/src/slic3r/GUI/ReleaseNote.cpp @@ -27,6 +27,7 @@ #include "DeviceCore/DevManager.h" #include "DeviceCore/DevStorage.h" +#include "md4c/src/md4c-html.h" namespace Slic3r { namespace GUI { @@ -268,32 +269,31 @@ UpdateVersionDialog::UpdateVersionDialog(wxWindow *parent) //webview m_vebview_release_note = CreateTipView(m_simplebook_release_note); - m_vebview_release_note->SetBackgroundColour(wxColour(0xF8, 0xF8, 0xF8)); m_vebview_release_note->SetSize(wxSize(FromDIP(560), FromDIP(430))); m_vebview_release_note->SetMinSize(wxSize(FromDIP(560), FromDIP(430))); //m_vebview_release_note->SetMaxSize(wxSize(FromDIP(560), FromDIP(430))); - m_vebview_release_note->Bind(wxEVT_WEBVIEW_NAVIGATING,[=](wxWebViewEvent& event){ - static bool load_url_first = false; - if(load_url_first){ - // Orca: not used in Orca Slicer - // wxLaunchDefaultBrowser(url_line); + + m_vebview_release_note->Bind(wxEVT_WEBVIEW_NAVIGATING,[=, count = 0](wxWebViewEvent& event) mutable { + count++; + if (count == 1) { + m_vebview_release_note->SetPage(wxString::FromUTF8(html_source), ""); + } else if (count >= 3) { + // Launch the default browser for links clicked by the user + wxLaunchDefaultBrowser(event.GetURL()); event.Veto(); - }else{ - load_url_first = true; } - }); - fs::path ph(data_dir()); - ph /= "resources/tooltip/releasenote.html"; - if (!fs::exists(ph)) { - ph = resources_dir(); - ph /= "tooltip/releasenote.html"; - } - auto url = ph.string(); - std::replace(url.begin(), url.end(), '\\', '/'); - url = "file:///" + url; - m_vebview_release_note->LoadURL(from_u8(url)); + // fs::path ph(data_dir()); + // ph /= "resources/tooltip/releasenote.html"; + // if (!fs::exists(ph)) { + // ph = resources_dir(); + // ph /= "tooltip/releasenote.html"; + // } + // auto url = ph.string(); + // std::replace(url.begin(), url.end(), '\\', '/'); + // url = "file:///" + url; + // m_vebview_release_note->LoadURL(from_u8(url)); m_simplebook_release_note->AddPage(m_scrollwindows_release_note, wxEmptyString, false); m_simplebook_release_note->AddPage(m_vebview_release_note, wxEmptyString, false); @@ -470,27 +470,31 @@ void UpdateVersionDialog::update_version_info(wxString release_note, wxString ve // } // } - if (use_web_link) { - m_brand->Hide(); - m_text_up_info->Hide(); - m_simplebook_release_note->SetSelection(1); - m_vebview_release_note->LoadURL(from_u8(url_line)); - } - else { - m_simplebook_release_note->SetMaxSize(wxSize(FromDIP(560), FromDIP(430))); - m_simplebook_release_note->SetSelection(0); - m_text_up_info->SetLabel(wxString::Format(_L("Click to download new version in default browser: %s"), version)); - wxBoxSizer* sizer_text_release_note = new wxBoxSizer(wxVERTICAL); - auto m_staticText_release_note = new ::Label(m_scrollwindows_release_note, release_note, LB_AUTO_WRAP); - m_staticText_release_note->SetMinSize(wxSize(FromDIP(560), -1)); - m_staticText_release_note->SetMaxSize(wxSize(FromDIP(560), -1)); - sizer_text_release_note->Add(m_staticText_release_note, 0, wxALL, 5); - m_scrollwindows_release_note->SetSizer(sizer_text_release_note); - m_scrollwindows_release_note->Layout(); - m_scrollwindows_release_note->Fit(); - SetMinSize(GetSize()); - SetMaxSize(GetSize()); - } + // if (use_web_link) { + // m_brand->Hide(); + // m_text_up_info->Hide(); + // m_simplebook_release_note->SetSelection(1); + // m_vebview_release_note->LoadURL(from_u8(url_line)); + // } + // else { + m_simplebook_release_note->SetMaxSize(wxSize(FromDIP(560), FromDIP(430))); + m_simplebook_release_note->SetSelection(1); + m_text_up_info->SetLabel(wxString::Format(_L("Click to download new version in default browser: %s"), version)); + auto data_buf_in = release_note.utf8_str(); + auto bg_color = StateColor::darkModeColorFor(*wxWHITE).GetAsString(); + auto fg_color = StateColor::darkModeColorFor(*wxBLACK).GetAsString(); + html_source = (boost::format("") + % fg_color % bg_color).str(); + md_html(data_buf_in.data(), data_buf_in.length(), [](const MD_CHAR* text, MD_SIZE size, void* userdata) { + std::string* out_buf = (std::string*)userdata; + out_buf->append(text, size); + }, (void*) &html_source, MD_DIALECT_GITHUB | MD_FLAG_STRIKETHROUGH | MD_FLAG_WIKILINKS, 0); + html_source.append(""); + m_vebview_release_note->LoadURL("file://" + (boost::filesystem::path (resources_dir()) / "web/guide/0/index.html").string()); + + SetMinSize(GetSize()); + SetMaxSize(GetSize()); + // } wxGetApp().UpdateDlgDarkUI(this); Layout(); diff --git a/src/slic3r/GUI/ReleaseNote.hpp b/src/slic3r/GUI/ReleaseNote.hpp index 37b7c58935..0c11dc2f58 100644 --- a/src/slic3r/GUI/ReleaseNote.hpp +++ b/src/slic3r/GUI/ReleaseNote.hpp @@ -107,6 +107,7 @@ public: Button* m_button_download; Button* m_button_cancel; std::string url_line; + std::string html_source; }; class SecondaryCheckDialog : public DPIFrame