diff --git a/resources/printers/BL-P001.json b/resources/printers/BL-P001.json index d1079d0380..795e4bb3ca 100644 --- a/resources/printers/BL-P001.json +++ b/resources/printers/BL-P001.json @@ -50,6 +50,7 @@ "model_id": "BL-P001", "compatible_machine": [ "BL-P002", "C11", "C12", "C13" ], "auto_cali_not_support_filaments": [ "GFU03", "GFU04" ], + "support_wrapping_deteciton": false, "printer_type": "3DPrinter-X1-Carbon", "printer_thumbnail_image": "printer_thumbnail", "printer_connect_help_image": "input_access_code_x1", diff --git a/resources/printers/BL-P002.json b/resources/printers/BL-P002.json index d5a4bb9b5a..d9309ea7ce 100644 --- a/resources/printers/BL-P002.json +++ b/resources/printers/BL-P002.json @@ -52,6 +52,7 @@ "auto_cali_not_support_filaments": [ "GFU03", "GFU04" ], "printer_type": "3DPrinter-X1", "printer_thumbnail_image": "printer_thumbnail", + "support_wrapping_deteciton": false, "printer_connect_help_image": "input_access_code_x1", "printer_use_ams_image": "ams_icon", "printer_ext_image": ["ext_image_xp"], diff --git a/resources/printers/C11.json b/resources/printers/C11.json index 68476c435e..cc616cb97a 100644 --- a/resources/printers/C11.json +++ b/resources/printers/C11.json @@ -43,6 +43,7 @@ "bed_temperature_limit": 100 }, "model_id": "C11", + "support_wrapping_deteciton": false, "compatible_machine": [ "BL-P001", "BL-P002", "C12", "C13" ], "auto_cali_not_support_filaments": [ "GFU03", "GFU04" ], "printer_type": "C11", diff --git a/resources/printers/C12.json b/resources/printers/C12.json index 80b83f83b3..a9fa87f311 100644 --- a/resources/printers/C12.json +++ b/resources/printers/C12.json @@ -43,6 +43,7 @@ "bed_temperature_limit": 100 }, "model_id": "C12", + "support_wrapping_deteciton": false, "compatible_machine": [ "BL-P001", "BL-P002", "C11", "C13" ], "auto_cali_not_support_filaments": [ "GFU03", "GFU04" ], "printer_type": "C12", diff --git a/resources/printers/C13.json b/resources/printers/C13.json index 53fc26a341..cbed61d686 100644 --- a/resources/printers/C13.json +++ b/resources/printers/C13.json @@ -52,6 +52,7 @@ "nozzle_max_temperature": 320 }, "model_id": "C13", + "support_wrapping_deteciton": false, "compatible_machine": [ "BL-P001", "BL-P002", "C11", "C12" ], "auto_cali_not_support_filaments": [ "GFU03", "GFU04" ], "printer_type": "C13", diff --git a/resources/printers/N1.json b/resources/printers/N1.json index a6c10bca8f..9f0a843219 100644 --- a/resources/printers/N1.json +++ b/resources/printers/N1.json @@ -43,6 +43,7 @@ "bed_temperature_limit": 80 }, "model_id": "N1", + "support_wrapping_deteciton": false, "compatible_machine": [], "auto_cali_not_support_filaments": [ "GFU03", "GFU04" ], "printer_type": "N1", diff --git a/resources/printers/N2S.json b/resources/printers/N2S.json index 1aa0210072..be4322b152 100644 --- a/resources/printers/N2S.json +++ b/resources/printers/N2S.json @@ -45,6 +45,7 @@ "model_id": "N2S", "compatible_machine": [], "auto_cali_not_support_filaments": [ "GFU03", "GFU04" ], + "support_wrapping_deteciton": false, "printer_type": "N2S", "ftp_folder": "sdcard/", "printer_thumbnail_image": "printer_thumbnail_n2s", diff --git a/resources/printers/O1D.json b/resources/printers/O1D.json index 87f4aa3c51..7b6f943f65 100644 --- a/resources/printers/O1D.json +++ b/resources/printers/O1D.json @@ -61,6 +61,7 @@ "support_user_preset": false }, "model_id": "O1D", + "support_wrapping_deteciton": true, "printer_modes": [ "fdm", "laser", "cut" ], "compatible_machine": [], "printer_type": "O1D", diff --git a/resources/profiles/BBL/machine/Bambu Lab H2D 0.4 nozzle.json b/resources/profiles/BBL/machine/Bambu Lab H2D 0.4 nozzle.json index 3937b36e3e..bac184e510 100644 --- a/resources/profiles/BBL/machine/Bambu Lab H2D 0.4 nozzle.json +++ b/resources/profiles/BBL/machine/Bambu Lab H2D 0.4 nozzle.json @@ -98,5 +98,6 @@ "machine_end_gcode": ";===== date: 2025/05/16 =====================\n;===== H2D =====================\nG392 S0 ;turn off nozzle clog detect\nM993 A0 B0 C0 ; nozzle cam detection not allowed.\n\nM400 ; wait for buffer to clear\nG92 E0 ; zero the extruder\nG1 E-0.8 F1800 ; retract\nG1 Z{max_layer_z + 0.5} F900 ; lower z a little\n\nG90\nM141 S0 ; turn off chamber heating\nM140 S0 ; turn off bed\nM106 S0 ; turn off fan\nM106 P2 S0 ; turn off remote part cooling fan\nM106 P3 S0 ; turn off chamber cooling fan\n\n; pull back filament to AMS\nM620 S65535\nT65535\nG150.2\nM621 S65535\n\nM620 S65279\nT65279\nG150.2\nM621 S65279\n\nG150.3\n\nM1002 judge_flag timelapse_record_flag\nM622 J1\n M400 ; wait all motion done\n M991 S0 P-1 ;end smooth timelapse at safe pos\n M400 S5 ;wait for last picture to be taken\nM623 ;end of \"timelapse_record_flag\"\n\nM104 S0 T0; turn off hotend\nM104 S0 T1; turn off hotend\n\nM400 ; wait all motion done\nM17 S\nM17 Z0.4 ; lower z motor current to reduce impact if there is something in the bottom\n{if (max_layer_z + 100.0) < 320}\n G1 Z{max_layer_z + 100.0} F600\n G1 Z{max_layer_z +98.0}\n{else}\n G1 Z320 F600\n G1 Z320\n{endif}\nM400 P100\nM17 R ; restore z current\n\nM220 S100 ; Reset feedrate magnitude\nM201.2 K1.0 ; Reset acc magnitude\nM73.2 R1.0 ;Reset left time magnitude\nM1002 set_gcode_claim_speed_level : 0\n\nM1015.4 S0 K0 ;disable air printing detect\n;=====printer finish sound=========\nM17\nM400 S1\nM1006 S1\nM1006 A53 B10 L99 C53 D10 M99 E53 F10 N99 \nM1006 A57 B10 L99 C57 D10 M99 E57 F10 N99 \nM1006 A0 B15 L0 C0 D15 M0 E0 F15 N0 \nM1006 A53 B10 L99 C53 D10 M99 E53 F10 N99 \nM1006 A57 B10 L99 C57 D10 M99 E57 F10 N99 \nM1006 A0 B15 L0 C0 D15 M0 E0 F15 N0 \nM1006 A48 B10 L99 C48 D10 M99 E48 F10 N99 \nM1006 A0 B15 L0 C0 D15 M0 E0 F15 N0 \nM1006 A60 B10 L99 C60 D10 M99 E60 F10 N99 \nM1006 W\n;=====printer finish sound=========\nM400\nM18\n\n", "layer_change_gcode": ";======== H2D 20250521========\n; layer num/total_layer_count: {layer_num+1}/[total_layer_count]\n; update layer progress\nM73 L{layer_num+1}\nM991 S0 P{layer_num} ;notify layer change\n\n", "time_lapse_gcode": ";======== H2D 20250702========\n; SKIPPABLE_START\n; SKIPTYPE: timelapse\nM622.1 S1 ; for prev firmware, default turned on\n\nM1002 judge_flag timelapse_record_flag\n\nM622 J1\nM993 A2 B2 C2\nM993 A0 B0 C0\n\n{if !spiral_mode && !(has_timelapse_safe_pos && timelapse_type == 0 && print_sequence != \"by object\") }\n {if most_used_physical_extruder_id!= curr_physical_extruder_id || timelapse_type == 1}\n M83\n G1 Z{max_layer_z + 0.4} F1200\n M400\n {endif}\n{endif}\n\n{if has_timelapse_safe_pos && timelapse_type == 0 && print_sequence != \"by object\"}\n M9711 M{timelapse_type} E{most_used_physical_extruder_id} X{timelapse_pos_x} Y{timelapse_pos_y} Z{layer_z + 0.4} S11 C10 O0 T3000\n{else}\n M9711 M{timelapse_type} E{most_used_physical_extruder_id} Z{layer_z + 0.4} S11 C10 O0 T3000\n{endif}\n\n{if !spiral_mode && !(has_timelapse_safe_pos && timelapse_type == 0 && print_sequence != \"by object\") }\n {if most_used_physical_extruder_id!= curr_physical_extruder_id || timelapse_type == 1}\n G90\n G1 Z{max_layer_z + 3.0} F1200\n G1 Y295 F30000\n G1 Y265 F18000\n M83\n {endif}\n{endif}\nM993 A3 B3 C3\n\nM623\n; SKIPPABLE_END\n", + "wrapping_detection_gcode": ";======== H2D 20250712 wrapping ========\n{if !spiral_mode}\nM622.1 S0 ; for previous firmware, default turn off\nM1002 set_flag g39_forced_detection_flag=1\nM1002 judge_flag g39_forced_detection_flag\nM622 J1\n{if layer_num == 3 || layer_num == 10 || layer_num == 19}\nM993 A2 B2 C2 ; nozzle cam detection allow status save.\nM993 A0 B0 C0 ; nozzle cam detection not allowed.\n\nM400 P100\nG90\nG1 Y320 F30000\nG1 Y327 F3000\n\nG39\n\nG90\nG1 Y295 F30000\nG1 Y265 F18000\n\nM993 A3 B3 C3 ; nozzle cam detection allow status restore.\n{endif}\nM623\n{endif}", "change_filament_gcode": ";======== H2D ========\n;===== 20250530 =====\nM993 A2 B2 C2 ; nozzle cam detection allow status save.\nM993 A0 B0 C0 ; nozzle cam detection not allowed.\n\n{if (filament_type[next_extruder] == \"PLA\") || (filament_type[next_extruder] == \"PETG\")\n || (filament_type[next_extruder] == \"PLA-CF\") || (filament_type[next_extruder] == \"PETG-CF\")}\nM1015.4 S1 K0 ;disable E air printing detect\n{else}\nM1015.4 S0 ; disable E air printing detect\n{endif}\n\nM620 S[next_extruder]A\nM1002 gcode_claim_action : 4\nM204 S9000\n\nG1 Z{max_layer_z + 3.0} F1200\n\nM400\nM106 P1 S0\nM106 P2 S0\n\n{if toolchange_count == 2}\n; get travel path for change filament\n;M620.1 X[travel_point_1_x] Y[travel_point_1_y] F21000 P0\n;M620.1 X[travel_point_2_x] Y[travel_point_2_y] F21000 P1\n;M620.1 X[travel_point_3_x] Y[travel_point_3_y] F21000 P2\n{endif}\n\n{if ((filament_type[current_extruder] == \"PLA\") || (filament_type[current_extruder] == \"PLA-CF\") || (filament_type[current_extruder] == \"PETG\")) && (nozzle_diameter[current_extruder] == 0.2)}\nM620.10 A0 F74.8347 L[flush_length] H{nozzle_diameter[current_extruder]} T{flush_temperatures[current_extruder]} P{nozzle_temperature[current_extruder]} S1\n{else}\nM620.10 A0 F{flush_volumetric_speeds[current_extruder]/2.4053*60} L[flush_length] H{nozzle_diameter[current_extruder]} T{flush_temperatures[current_extruder]} P{nozzle_temperature[current_extruder]} S1\n{endif}\n\n{if ((filament_type[next_extruder] == \"PLA\") || (filament_type[next_extruder] == \"PLA-CF\") || (filament_type[next_extruder] == \"PETG\")) && (nozzle_diameter[next_extruder] == 0.2)}\nM620.10 A1 F74.8347 L[flush_length] H{nozzle_diameter[next_extruder]} T{flush_temperatures[next_extruder]} P{nozzle_temperature[next_extruder]} S1\n{else}\nM620.10 A1 F{flush_volumetric_speeds[next_extruder]/2.4053*60} L[flush_length] H{nozzle_diameter[next_extruder]} T{flush_temperatures[next_extruder]} P{nozzle_temperature[next_extruder]} S1\n{endif}\n\n{if long_retraction_when_cut}\nM620.11 P1 I[current_extruder] E-{retraction_distance_when_cut} F{max((flush_volumetric_speeds[current_extruder]/2.4053*60), 200)}\n{else}\nM620.11 P0 I[current_extruder] E0\n{endif}\n\n{if long_retraction_when_ec}\nM620.11 K1 I[current_extruder] R{retraction_distance_when_ec} F{max((flush_volumetric_speeds[current_extruder]/2.4053*60), 200)}\n{else}\nM620.11 K0 I[current_extruder] R0\n{endif}\n\nM628 S1\n{if filament_type[current_extruder] == \"TPU\"}\nM620.11 S0 L0 I[current_extruder] E-{retraction_distances_when_cut[current_extruder]} F{max((flush_volumetric_speeds[current_extruder]/2.4053*60), 200)}\n{else}\n{if (filament_type[current_extruder] == \"PA\") || (filament_type[current_extruder] == \"PA-GF\")}\nM620.11 S1 L0 I[current_extruder] R4 D2 E-{retraction_distances_when_cut[current_extruder]} F{max((flush_volumetric_speeds[current_extruder]/2.4053*60), 200)}\n{else}\nM620.11 S1 L0 I[current_extruder] R10 D8 E-{retraction_distances_when_cut[current_extruder]} F{max((flush_volumetric_speeds[current_extruder]/2.4053*60), 200)}\n{endif}\n{endif}\nM629\n\n{if filament_type[current_extruder] == \"TPU\" || filament_type[next_extruder] == \"TPU\"}\nM620.11 H2 C331\n{else}\nM620.11 H0\n{endif}\n\nT[next_extruder]\n\n;deretract\n{if filament_type[next_extruder] == \"TPU\"}\n{else}\n{if (filament_type[next_extruder] == \"PA\") || (filament_type[next_extruder] == \"PA-GF\")}\n;VG1 E1 F{max(new_filament_e_feedrate, 200)}\n;VG1 E1 F{max(new_filament_e_feedrate/2, 100)}\n{else}\n;VG1 E4 F{max(new_filament_e_feedrate, 200)}\n;VG1 E4 F{max(new_filament_e_feedrate/2, 100)}\n{endif}\n{endif}\n\n; VFLUSH_START\n\n{if flush_length>41.5}\n;VG1 E41.5 F{min(old_filament_e_feedrate,new_filament_e_feedrate)}\n;VG1 E{flush_length-41.5} F{new_filament_e_feedrate}\n{else}\n;VG1 E{flush_length} F{min(old_filament_e_feedrate,new_filament_e_feedrate)}\n{endif}\n\nSYNC T{ceil(flush_length / 125) * 5}\n\n; VFLUSH_END\n\nM1002 set_filament_type:{filament_type[next_extruder]}\n\nM400\nM83\n{if next_extruder < 255}\n\n\nM628 S0\n;VM109 S[new_filament_temp]\nM629\nM400\n\nM983.3 F{filament_max_volumetric_speed[next_extruder]/2.4} A0.4\n\n\nM400\n{if wipe_avoid_perimeter}\nG1 Y320 F30000\nG1 X{wipe_avoid_pos_x} F30000\n{endif}\nG1 Y295 F30000\nG1 Y265 F18000\nG1 Z{max_layer_z + 3.0} F3000\n{if layer_z <= (initial_layer_print_height + 0.001)}\nM204 S[initial_layer_acceleration]\n{else}\nM204 S[default_acceleration]\n{endif}\n{else}\nG1 X[x_after_toolchange] Y[y_after_toolchange] Z[z_after_toolchange] F12000\n{endif}\nM621 S[next_extruder]A\n\nM993 A3 B3 C3 ; nozzle cam detection allow status restore.\n\n{if (filament_type[next_extruder] == \"TPU\")}\nM1015.3 S1;enable tpu clog detect\n{else}\nM1015.3 S0;disable tpu clog detect\n{endif}\n\n{if (filament_type[next_extruder] == \"PLA\") || (filament_type[next_extruder] == \"PETG\")\n || (filament_type[next_extruder] == \"PLA-CF\") || (filament_type[next_extruder] == \"PETG-CF\")}\nM1015.4 S1 K1 H[nozzle_diameter] ;enable E air printing detect\n{else}\nM1015.4 S0 ; disable E air printing detect\n{endif}\n\nM620.6 I[next_extruder] W1 ;enable ams air printing detect\nM1002 gcode_claim_action : 0\n" } \ No newline at end of file diff --git a/resources/profiles/BBL/machine/fdm_machine_common.json b/resources/profiles/BBL/machine/fdm_machine_common.json index 260c3ee5d5..35346a87fe 100644 --- a/resources/profiles/BBL/machine/fdm_machine_common.json +++ b/resources/profiles/BBL/machine/fdm_machine_common.json @@ -143,6 +143,7 @@ "machine_start_gcode": "G0 Z20 F9000\nG92 E0; G1 E-10 F1200\nG28\nM970 Q1 A10 B10 C130 K0\nM970 Q1 A10 B131 C250 K1\nM974 Q1 S1 P0\nM970 Q0 A10 B10 C130 H20 K0\nM970 Q0 A10 B131 C250 K1\nM974 Q0 S1 P0\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\nG29 ;Home\nG90;\nG92 E0 ;Reset Extruder \nG1 Z2.0 F3000 ;Move Z Axis up \nG1 X10.1 Y20 Z0.28 F5000.0 ;Move to start position\nM109 S205;\nG1 X10.1 Y200.0 Z0.28 F1500.0 E15 ;Draw the first line\nG1 X10.4 Y200.0 Z0.28 F5000.0 ;Move to side a little\nG1 X10.4 Y20 Z0.28 F1500.0 E30 ;Draw the second line\nG92 E0 ;Reset Extruder \nG1 X110 Y110 Z2.0 F3000 ;Move Z Axis up", "machine_end_gcode": "M400 ; wait for buffer to clear\nG92 E0 ; zero the extruder\nG1 E-4.0 F3600; retract \nG91\nG1 Z3;\nM104 S0 ; turn off hotend\nM140 S0 ; turn off bed\nM106 S0 ; turn off fan\nG90 \nG0 X110 Y200 F3600 \nprint_end", "time_lapse_gcode": "", + "wrapping_detection_gcode": "", "change_filament_gcode": "", "purge_in_prime_tower": "0", "enable_filament_ramming": "0" diff --git a/src/OrcaSlicer.cpp b/src/OrcaSlicer.cpp index 44c914cdfb..4ca0fb787b 100644 --- a/src/OrcaSlicer.cpp +++ b/src/OrcaSlicer.cpp @@ -532,7 +532,7 @@ static int load_key_values_from_json(const std::string &file, std::map gcodes_key_set = {"filament_end_gcode", "filament_start_gcode", "change_filament_gcode", "layer_change_gcode", "machine_end_gcode", "machine_pause_gcode", "machine_start_gcode", - "template_custom_gcode", "printing_by_object_gcode", "before_layer_change_gcode", "time_lapse_gcode"}; + "template_custom_gcode", "printing_by_object_gcode", "before_layer_change_gcode", "time_lapse_gcode", "wrapping_detection_gcode"}; static void load_default_gcodes_to_config(DynamicPrintConfig& config, Preset::Type type) { @@ -569,6 +569,9 @@ static void load_default_gcodes_to_config(DynamicPrintConfig& config, Preset::Ty ConfigOptionString* timeplase_gcode_opt = config.option("time_lapse_gcode", true); BOOST_LOG_TRIVIAL(trace) << __FUNCTION__<< ", time_lapse_gcode: "<value; + + ConfigOptionString *wrapping_detection_gcode_opt = config.option("wrapping_detection_gcode", true); + BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << ", wrapping_detection_gcode: " << wrapping_detection_gcode_opt->value; } else if (type == Preset::TYPE_FILAMENT) { @@ -3631,7 +3634,10 @@ int CLI::run(int argc, char **argv) ConfigOptionFloats* volume_option = print_config.option("filament_prime_volume", true); std::vector wipe_volume = volume_option->values; - Vec3d wipe_tower_size = plate->estimate_wipe_tower_size(print_config, plate_obj_size_info.wipe_width, get_max_element(wipe_volume), new_extruder_count, filaments_cnt); + const ConfigOptionBool * wrapping_detection = print_config.option("enable_wrapping_detection"); + bool enable_wrapping = (wrapping_detection != nullptr) && wrapping_detection->value; + + Vec3d wipe_tower_size = plate->estimate_wipe_tower_size(print_config, plate_obj_size_info.wipe_width, get_max_element(wipe_volume), new_extruder_count, filaments_cnt, false, enable_wrapping); plate_obj_size_info.wipe_width = wipe_tower_size(0); plate_obj_size_info.wipe_depth = wipe_tower_size(1); @@ -4771,7 +4777,10 @@ int CLI::run(int argc, char **argv) //float depth = v * (filaments_cnt - 1) / (layer_height * w); - Vec3d wipe_tower_size = cur_plate->estimate_wipe_tower_size(m_print_config, w, get_max_element(v), new_extruder_count, filaments_cnt); + const ConfigOptionBool *wrapping_detection = m_print_config.option("enable_wrapping_detection"); + bool enable_wrapping = (wrapping_detection != nullptr) && wrapping_detection->value; + + Vec3d wipe_tower_size = cur_plate->estimate_wipe_tower_size(m_print_config, w, get_max_element(v), new_extruder_count, filaments_cnt, false, enable_wrapping); Vec3d plate_origin = cur_plate->get_origin(); int plate_width, plate_depth, plate_height; partplate_list.get_plate_size(plate_width, plate_depth, plate_height); diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 6e52342a20..e886526895 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1322,7 +1322,7 @@ static std::vector get_path_of_change_filament(const Print& print) wipe_tower_z = m_last_wipe_tower_print_z + m_tool_changes[m_layer_idx].front().layer_height; } - if (m_enable_timelapse_print && m_is_first_print) { + if ((m_enable_timelapse_print || m_enable_wrapping_detection) && m_is_first_print) { gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][0], m_tool_changes[m_layer_idx][0].new_tool, wipe_tower_z); m_tool_change_idx++; m_is_first_print = false; @@ -1353,7 +1353,7 @@ static std::vector get_path_of_change_filament(const Print& print) ignore_sparse = (m_tool_changes[m_layer_idx].size() == 1 && m_tool_changes[m_layer_idx].front().initial_tool == m_tool_changes[m_layer_idx].front().new_tool); } - if (m_enable_timelapse_print && m_is_first_print) { + if ((m_enable_timelapse_print || m_enable_wrapping_detection) && m_is_first_print) { return false; } @@ -1752,6 +1752,7 @@ namespace DoExport { if (ret.size() < MAX_TAGS_COUNT) check(_(L("Before layer change G-code")), config.before_layer_change_gcode.value); if (ret.size() < MAX_TAGS_COUNT) check(_(L("Layer change G-code")), config.layer_change_gcode.value); if (ret.size() < MAX_TAGS_COUNT) check(_(L("Timelapse G-code")), config.time_lapse_gcode.value); + if (ret.size() < MAX_TAGS_COUNT) check(_(L("Timelapse G-code")), config.wrapping_detection_gcode.value); if (ret.size() < MAX_TAGS_COUNT) check(_(L("Change filament G-code")), config.change_filament_gcode.value); if (ret.size() < MAX_TAGS_COUNT) check(_(L("Printing by object G-code")), config.printing_by_object_gcode.value); //if (ret.size() < MAX_TAGS_COUNT) check(_(L("Color Change G-code")), config.color_change_gcode.value); @@ -3015,7 +3016,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Prusa Multi-Material wipe tower. if (has_wipe_tower && ! layers_to_print.empty()) { m_wipe_tower.reset(new WipeTowerIntegration(print.config(), print.get_plate_index(), print.get_plate_origin(), *print.wipe_tower_data().priming.get(), - print.wipe_tower_data().tool_changes, *print.wipe_tower_data().final_purge.get())); + print.wipe_tower_data().tool_changes, *print.wipe_tower_data().final_purge.get(), print.get_slice_used_filaments(false))); m_wipe_tower->set_wipe_tower_depth(print.get_wipe_tower_depth()); m_wipe_tower->set_wipe_tower_bbx(print.get_wipe_tower_bbx()); m_wipe_tower->set_rib_offset(print.get_rib_offset()); @@ -4688,6 +4689,30 @@ LayerResult GCode::process_layer( if (m_wipe_tower) m_wipe_tower->set_is_first_print(true); + auto insert_wrapping_detection_gcode = [this, &print, &print_z, &most_used_extruder]() -> std::string { + std::string wrapping_gcode; + if (print.config().enable_wrapping_detection && !print.config().wrapping_detection_gcode.value.empty()) { + DynamicConfig config; + config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); + config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); + config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z)); + config.set_key_value("most_used_physical_extruder_id", new ConfigOptionInt(m_config.physical_extruder_map.get_at(most_used_extruder))); + config.set_key_value("curr_physical_extruder_id", new ConfigOptionInt(m_config.physical_extruder_map.get_at(m_writer.filament()->extruder_id()))); + wrapping_gcode = this->placeholder_parser_process("wrapping_detection_gcode", print.config().wrapping_detection_gcode.value, m_writer.filament()->id(), &config) +"\n"; + } + m_writer.set_current_position_clear(false); + + double temp_z_after_tool_change; + if (GCodeProcessor::get_last_z_from_gcode(wrapping_gcode, temp_z_after_tool_change)) { + Vec3d pos = m_writer.get_position(); + pos(2) = temp_z_after_tool_change; + m_writer.set_position(pos); + } + return wrapping_gcode; + }; + + bool has_insert_wrapping_detection_gcode = false; + // Extrude the skirt, brim, support, perimeters, infill ordered by the extruders. for (unsigned int extruder_id : layer_tools.extruders) { @@ -4714,6 +4739,12 @@ LayerResult GCode::process_layer( has_insert_timelapse_gcode = true; } } + + if (!has_insert_wrapping_detection_gcode) { + gcode += this->retract(false, false, auto_lift_type); + gcode += insert_wrapping_detection_gcode(); + has_insert_wrapping_detection_gcode = true; + } gcode_toolchange = m_wipe_tower->tool_change(*this, extruder_id, extruder_id == layer_tools.extruders.back()); } } else { @@ -4726,6 +4757,13 @@ LayerResult GCode::process_layer( gcode += insert_timelapse_gcode(); has_insert_timelapse_gcode = true; } + + if (!has_insert_wrapping_detection_gcode) { + gcode += this->retract(false, false, auto_lift_type); + gcode += insert_wrapping_detection_gcode(); + has_insert_wrapping_detection_gcode = true; + } + gcode_toolchange = this->set_extruder(extruder_id, print_z); } if (!gcode_toolchange.empty()) { diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index a6e2885477..b5768e7556 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -81,7 +81,8 @@ public: const Vec3d plate_origin, const std::vector &priming, const std::vector> &tool_changes, - const WipeTower::ToolChangeResult &final_purge) : + const WipeTower::ToolChangeResult &final_purge, + const std::vector &slice_used_filaments) : m_left(/*float(print_config.wipe_tower_x.value)*/ 0.f), m_right(float(/*print_config.wipe_tower_x.value +*/ print_config.prime_tower_width.value)), m_wipe_tower_pos(float(print_config.wipe_tower_x.get_at(plate_idx)), float(print_config.wipe_tower_y.get_at(plate_idx))), @@ -94,6 +95,7 @@ public: m_plate_origin(plate_origin), m_single_extruder_multi_material(print_config.single_extruder_multi_material), m_enable_timelapse_print(print_config.timelapse_type.value == TimelapseType::tlSmooth), + m_enable_wrapping_detection(print_config.enable_wrapping_detection && (slice_used_filaments.size() <= 1)), m_is_first_print(true), m_print_config(&print_config) { @@ -147,6 +149,7 @@ private: Vec3d m_plate_origin; bool m_single_extruder_multi_material; bool m_enable_timelapse_print; + bool m_enable_wrapping_detection; bool m_is_first_print; const PrintConfig * m_print_config; float m_wipe_tower_depth; diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 909d29e619..ea3a3b3542 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -780,9 +780,18 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_ for (int i = int(m_layer_tools.size()) - 2; i >= 0; -- i) m_layer_tools[i].wipe_tower_partitions = std::max(m_layer_tools[i + 1].wipe_tower_partitions, m_layer_tools[i].wipe_tower_partitions); + + int wrapping_layer_nums = config.wrapping_detection_layers; + for (size_t i = 0; i < wrapping_layer_nums; ++i) { + if (i >= m_layer_tools.size()) + break; + LayerTools < = m_layer_tools[i]; + lt.has_wipe_tower = config.enable_wrapping_detection; + } + //FIXME this is a hack to get the ball rolling. for (LayerTools < : m_layer_tools) - lt.has_wipe_tower = (lt.has_object && (config.timelapse_type == TimelapseType::tlSmooth || lt.wipe_tower_partitions > 0)) + lt.has_wipe_tower |= (lt.has_object && (config.timelapse_type == TimelapseType::tlSmooth || lt.wipe_tower_partitions > 0)) || lt.print_z < object_bottom_z + EPSILON; // Test for a raft, insert additional wipe tower layer to fill in the raft separation gap. @@ -840,7 +849,8 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_ double last_wipe_tower_print_z = lt_next.print_z; while (++j < m_layer_tools.size()-1 && !m_layer_tools[j].has_wipe_tower) if (m_layer_tools[j+1].print_z - last_wipe_tower_print_z > max_layer_height + EPSILON) { - m_layer_tools[j].has_wipe_tower = true; + if (!config.enable_wrapping_detection) + m_layer_tools[j].has_wipe_tower = true; last_wipe_tower_print_z = m_layer_tools[j].print_z; } } diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index f640cc0bdb..4b3350a63d 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -428,7 +428,7 @@ static Polylines remove_points_from_polygon(const Polygon &polygon, const std::v for (auto &p : tmp_poly) points.push_back(unscale(p).cast()); points.pop_back(); } - + for (int i = 0; i < skip_points.size(); i++) { for (int j = 0; j < points.size(); j++) { Vec2f& p1 = points[j]; @@ -1315,6 +1315,9 @@ WipeTower::ToolChangeResult WipeTower::construct_block_tcr(WipeTowerWriter &writ return result; } +// BBS +const double wrapping_wipe_tower_depth = 10; + // BBS const std::map WipeTower::min_depth_per_height = { {5.f,5.f}, {100.f, 20.f}, {250.f, 40.f}, {350.f, 60.f} @@ -1479,7 +1482,7 @@ TriangleMesh WipeTower::its_make_rib_brim(const Polygon& brim, float layer_heigh } -WipeTower::WipeTower(const PrintConfig& config, int plate_idx, Vec3d plate_origin, size_t initial_tool, const float wipe_tower_height) : +WipeTower::WipeTower(const PrintConfig& config, int plate_idx, Vec3d plate_origin, size_t initial_tool, const float wipe_tower_height, const std::vector& slice_used_filaments) : m_semm(config.single_extruder_multi_material.value), m_wipe_tower_pos(config.wipe_tower_x.get_at(plate_idx), config.wipe_tower_y.get_at(plate_idx)), m_wipe_tower_width(float(config.prime_tower_width)), @@ -1497,6 +1500,9 @@ WipeTower::WipeTower(const PrintConfig& config, int plate_idx, Vec3d plate_origi m_current_tool(initial_tool), //wipe_volumes(flush_matrix) m_enable_timelapse_print(config.timelapse_type.value == TimelapseType::tlSmooth), + m_enable_wrapping_detection(config.enable_wrapping_detection), + m_wrapping_detection_layers(config.wrapping_detection_layers.value), + m_slice_used_filaments(slice_used_filaments.size()), m_filaments_change_length(config.filament_change_length.values), m_is_multi_extruder(config.nozzle_diameter.size() > 1), m_use_gap_wall(config.prime_tower_skip_points.value), @@ -2524,6 +2530,9 @@ void WipeTower::plan_tower() float min_wipe_tower_depth = WipeTower::get_limit_depth_by_height(m_wipe_tower_height); { + if (m_enable_wrapping_detection && max_depth < EPSILON) + max_depth = wrapping_wipe_tower_depth; + if (m_enable_timelapse_print && max_depth < EPSILON) max_depth = min_wipe_tower_depth; @@ -2575,6 +2584,9 @@ void WipeTower::plan_tower() for (int layer_index = int(m_plan.size()) - 1; layer_index >= 0; --layer_index) { float this_layer_depth = std::max(m_plan[layer_index].depth, m_plan[layer_index].toolchanges_depth()); + if (m_enable_wrapping_detection && (layer_index < m_wrapping_detection_layers) && this_layer_depth < EPSILON) + this_layer_depth = wrapping_wipe_tower_depth; + if (m_enable_timelapse_print && this_layer_depth < EPSILON) this_layer_depth = min_wipe_tower_depth; @@ -2593,6 +2605,14 @@ void WipeTower::plan_tower() max_depth_for_all = m_plan[0].depth; } + if (m_enable_wrapping_detection) { + for (int i = m_wrapping_detection_layers - 1; i >= 0; i--) { + if (m_plan.size() <= m_wrapping_detection_layers && (m_plan[i].depth < wrapping_wipe_tower_depth)) { + m_plan[i].depth = wrapping_wipe_tower_depth; + } + } + } + if (m_enable_timelapse_print) { for (int i = int(m_plan.size()) - 1; i >= 0; i--) { m_plan[i].depth = max_depth_for_all; @@ -3556,7 +3576,7 @@ void WipeTower::update_all_layer_depth(float wipe_tower_depth) if (m_wipe_tower_depth > 0) m_wipe_tower_depth += start_offset; - if (m_enable_timelapse_print) { + if (m_enable_wrapping_detection || m_enable_timelapse_print) { if (is_approx(m_wipe_tower_depth, 0.f)) m_wipe_tower_depth = wipe_tower_depth; for (WipeTowerInfo &plan_info : m_plan) { @@ -3700,6 +3720,13 @@ void WipeTower::plan_tower_new() // only for get m_extra_spacing { + if (m_enable_wrapping_detection && max_depth < EPSILON) { + max_depth = wrapping_wipe_tower_depth; + if (m_use_rib_wall) { + m_wipe_tower_width = max_depth; + } + } + if (m_enable_timelapse_print && max_depth < EPSILON) { max_depth = min_wipe_tower_depth; if (m_use_rib_wall) { m_wipe_tower_width = max_depth; } @@ -3877,6 +3904,7 @@ void WipeTower::generate_new(std::vectorm_cur_layer_id] = block.start_depth; }); @@ -3903,13 +3931,13 @@ void WipeTower::generate_new(std::vectordepth + m_perimeter_width; - if (is_new_mode && m_enable_timelapse_print) + if (is_new_mode && (m_enable_timelapse_print || m_enable_wrapping_detection)) wipe_tower_depth = m_wipe_tower_depth; box_coordinates wt_box(Vec2f(0.f, (m_current_shape == SHAPE_REVERSED ? m_layer_info->toolchanges_depth() : 0.f)), m_wipe_tower_width, wipe_tower_depth); wt_box = align_perimeter(wt_box); diff --git a/src/libslic3r/GCode/WipeTower.hpp b/src/libslic3r/GCode/WipeTower.hpp index 1492d21788..d46a38b5ae 100644 --- a/src/libslic3r/GCode/WipeTower.hpp +++ b/src/libslic3r/GCode/WipeTower.hpp @@ -175,7 +175,7 @@ public: // width -- width of wipe tower in mm ( default 60 mm - leave as it is ) // wipe_area -- space available for one toolchange in mm // BBS: add partplate logic - WipeTower(const PrintConfig& config, int plate_idx, Vec3d plate_origin, size_t initial_tool, const float wipe_tower_height); + WipeTower(const PrintConfig& config, int plate_idx, Vec3d plate_origin, size_t initial_tool, const float wipe_tower_height, const std::vector& slice_used_filaments); // Set the extruder properties. @@ -405,6 +405,9 @@ private: return m_filpar[0].filament_area; // all extruders are assumed to have the same filament diameter at this point } + int m_slice_used_filaments = 0; + int m_wrapping_detection_layers = 0; + bool m_enable_wrapping_detection = false; bool m_enable_timelapse_print = false; bool m_semm = true; // Are we using a single extruder multimaterial printer? bool m_purge_in_prime_tower = false; // Do we purge in the prime tower? diff --git a/src/libslic3r/ParameterUtils.cpp b/src/libslic3r/ParameterUtils.cpp index 2e7b0d1d3c..d15f26d738 100644 --- a/src/libslic3r/ParameterUtils.cpp +++ b/src/libslic3r/ParameterUtils.cpp @@ -75,4 +75,33 @@ int get_index_for_extruder_parameter(const DynamicPrintConfig &config, const std return variant_index; } +std::vector get_wrapping_detection_area(const std::vector &wrapping_detection_path, double avoidance_radius) +{ + if (wrapping_detection_path.empty()) + return std::vector(); + + double min_x = wrapping_detection_path[0](0); + double max_x = wrapping_detection_path[0](0); + double min_y = wrapping_detection_path[0](1); + double max_y = wrapping_detection_path[0](1); + + for (const Vec2d& pt : wrapping_detection_path) { + if (pt(0) < min_x) + min_x = pt(0); + if (pt(0) > max_x) + max_x = pt(0); + if (pt(1) < min_y) + min_y = pt(1); + if (pt(1) > max_y) + max_y = pt(1); + } + + min_x = min_x - avoidance_radius; + min_y = min_y - avoidance_radius; + max_x = max_x + avoidance_radius; + max_y = max_y + avoidance_radius; + + return {{min_x, min_y}, {max_x, min_y}, {max_x, max_y}, {min_x, max_y}}; +} + }; // namespace Slic3r diff --git a/src/libslic3r/ParameterUtils.hpp b/src/libslic3r/ParameterUtils.hpp index 795e7ff619..001df5e0d3 100644 --- a/src/libslic3r/ParameterUtils.hpp +++ b/src/libslic3r/ParameterUtils.hpp @@ -11,6 +11,8 @@ std::vector get_other_layers_print_sequence(int sequence_num void get_other_layers_print_sequence(const std::vector &customize_sequences, int &sequence_nums, std::vector &sequence); extern int get_index_for_extruder_parameter(const DynamicPrintConfig &config, const std::string &opt_key, int cur_extruder_id, ExtruderType extruder_type, NozzleVolumeType nozzle_volume_type); + +extern std::vector get_wrapping_detection_area(const std::vector &wrapping_detection_path, double avoidance_radius); } // namespace Slic3r #endif // slic3r_Parameter_Utils_hpp_ diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 0f36bb13dc..9817f69203 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -997,13 +997,13 @@ static std::vector s_Preset_printer_options { "printer_technology", "printable_area", "extruder_printable_area", "bed_exclude_area","bed_custom_texture", "bed_custom_model", "gcode_flavor", "fan_kickstart", "fan_speedup_time", "fan_speedup_overhangs", - "single_extruder_multi_material", "manual_filament_change", "machine_start_gcode", "machine_end_gcode", "before_layer_change_gcode", "printing_by_object_gcode", "layer_change_gcode", "time_lapse_gcode", "change_filament_gcode", "change_extrusion_role_gcode", + "single_extruder_multi_material", "manual_filament_change", "machine_start_gcode", "machine_end_gcode", "before_layer_change_gcode", "printing_by_object_gcode", "layer_change_gcode", "time_lapse_gcode", "wrapping_detection_gcode", "change_filament_gcode", "change_extrusion_role_gcode", "printer_model", "printer_variant", "printer_extruder_id", "printer_extruder_variant", "extruder_variant_list", "default_nozzle_volume_type", "printable_height", "extruder_printable_height", "extruder_clearance_radius", "extruder_clearance_height_to_lid", "extruder_clearance_height_to_rod", "nozzle_height", "master_extruder_id", "default_print_profile", "inherits", "silent_mode", - "scan_first_layer", "machine_load_filament_time", "machine_unload_filament_time", "machine_tool_change_time", "time_cost", "machine_pause_gcode", "template_custom_gcode", + "scan_first_layer", "enable_wrapping_detection", "wrapping_detection_layers", "wrapping_detection_path", "machine_load_filament_time", "machine_unload_filament_time", "machine_tool_change_time", "time_cost", "machine_pause_gcode", "template_custom_gcode", "nozzle_type", "nozzle_hrc","auxiliary_fan", "nozzle_volume","upward_compatible_machine", "z_hop_types", "travel_slope", "retract_lift_enforce","support_chamber_temp_control","support_air_filtration","printer_structure", "best_object_pos", "head_wrap_detect_zone", "host_type", "print_host", "printhost_apikey", "bbl_use_printhost", diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 6a169ce8cc..937cd34ad4 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -1076,7 +1076,7 @@ void PresetBundle::update_system_preset_setting_ids(std::map gcodes_key_set = {"filament_end_gcode", "filament_start_gcode", "change_filament_gcode", "layer_change_gcode", "machine_end_gcode", "machine_pause_gcode", "machine_start_gcode", - "template_custom_gcode", "printing_by_object_gcode", "before_layer_change_gcode", "time_lapse_gcode"}; + "template_custom_gcode", "printing_by_object_gcode", "before_layer_change_gcode", "time_lapse_gcode", "wrapping_detection_gcode"}; int PresetBundle::validate_presets(const std::string &file_name, DynamicPrintConfig& config, std::set& different_gcodes) { bool validated = false; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index b816b6981b..5d368154b3 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -36,6 +36,7 @@ #include "nlohmann/json.hpp" #include "GCode/ConflictChecker.hpp" +#include "ParameterUtils.hpp" #include @@ -136,6 +137,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n "inner_wall_acceleration", "initial_layer_acceleration", "top_surface_acceleration", + "enable_wrapping_detection", "bridge_acceleration", "travel_acceleration", "sparse_infill_acceleration", @@ -150,6 +152,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n "gcode_add_line_number", "layer_change_gcode", "time_lapse_gcode", + "wrapping_detection_gcode", "fan_min_speed", "fan_max_speed", "printable_height", @@ -919,6 +922,13 @@ static StringObjectException layered_print_cleareance_valid(const Print &print, std::for_each(exclude_polys.begin(), exclude_polys.end(), [&print_origin](Polygon& p) { p.translate(scale_(print_origin.x()), scale_(print_origin.y())); }); + Pointfs wrapping_detection_area = get_wrapping_detection_area(print_config.wrapping_detection_path.values, print_config.extruder_clearance_radius.value / 2); + Polygon wrapping_poly; + for (size_t i = 0; i < wrapping_detection_area.size(); ++i) { + auto pt = wrapping_detection_area[i]; + wrapping_poly.points.emplace_back(scale_(pt.x() + print_origin.x()), scale_(pt.y() + print_origin.y())); + } + std::map map_model_object_to_convex_hull; // sequential_print_horizontal_clearance_valid Polygons convex_hulls_other; @@ -959,6 +969,11 @@ static StringObjectException layered_print_cleareance_valid(const Print &print, warning->object = inst->model_instance->get_object(); }*/ } + + if (!intersection(wrapping_poly, convex_hull).empty()) { + return {inst->model_instance->get_object()->name + L(" is too close to wrapping detection area, there may be collisions when printing.") + "\n", + inst->model_instance->get_object()}; + } convex_hulls_other.emplace_back(convex_hull); } @@ -991,7 +1006,9 @@ static StringObjectException layered_print_cleareance_valid(const Print &print, convex_hulls_temp.push_back(wipe_tower_convex_hull); } else { //here, wipe_tower_polygon is not always convex. - Polygon wipe_tower_polygon = print.wipe_tower_data().wipe_tower_mesh_data->bottom; + Polygon wipe_tower_polygon; + if (print.wipe_tower_data().wipe_tower_mesh_data) + wipe_tower_polygon = print.wipe_tower_data().wipe_tower_mesh_data->bottom; wipe_tower_polygon.translate(Point(scale_(x), scale_(y))); convex_hulls_temp.push_back(wipe_tower_polygon); } @@ -1007,7 +1024,9 @@ static StringObjectException layered_print_cleareance_valid(const Print &print, }*/ return {L("Prime Tower") + L(" is too close to exclusion area, and collisions will be caused.\n")}; } - + if (!intersection({wrapping_poly}, convex_hulls_temp).empty()) { + return {L("Prime Tower") + L(" is too close to wrapping detection area, and collisions will be caused.\n")}; + } return {}; } @@ -1141,10 +1160,13 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons* } } - if (m_config.print_sequence == PrintSequence::ByObject) { + if (m_config.print_sequence == PrintSequence::ByObject && m_objects.size() > 1) { if (m_config.timelapse_type == TimelapseType::tlSmooth) return {L("Smooth mode of timelapse is not supported when \"by object\" sequence is enabled.")}; + if (m_config.enable_wrapping_detection) + return {L("Wrapping detection is not supported when \"by object\" sequence is enabled.")}; + //BBS: refine seq-print validation logic auto ret = sequential_print_clearance_valid(*this, collison_polygons, height_polygons); if (!ret.string.empty()) { @@ -2890,6 +2912,9 @@ size_t Print::get_extruder_id(unsigned int filament_id) const bool Print::has_wipe_tower() const { if (m_config.enable_prime_tower.value == true) { + if (m_config.enable_wrapping_detection.value) + return true; + if (enable_timelapse_print()) return true; @@ -3016,7 +3041,7 @@ void Print::_make_wipe_tower() if (!bUseWipeTower2) { // in BBL machine, wipe tower is only use to prime extruder. So just use a global wipe volume. WipeTower wipe_tower(m_config, m_plate_index, m_origin, m_wipe_tower_data.tool_ordering.first_extruder(), - m_wipe_tower_data.tool_ordering.empty() ? 0.f : m_wipe_tower_data.tool_ordering.back().print_z); + m_wipe_tower_data.tool_ordering.empty() ? 0.f : m_wipe_tower_data.tool_ordering.back().print_z, m_wipe_tower_data.tool_ordering.all_extruders()); wipe_tower.set_has_tpu_filament(this->has_tpu_filament()); wipe_tower.set_filament_map(this->get_filament_maps()); // Set the extruder & material properties at the wipe tower object. @@ -3087,7 +3112,7 @@ void Print::_make_wipe_tower() layer_tools.wiping_extrusions().ensure_perimeters_infills_order(*this); // if enable timelapse, slice all layer - if (enable_timelapse_print()) { + if (m_config.enable_wrapping_detection || enable_timelapse_print()) { if (layer_tools.wipe_tower_partitions == 0) wipe_tower.set_last_layer_extruder_fill(false); continue; } diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 7c109a8ac5..0c4b4e9355 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -3562,6 +3562,26 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloatOrPercent(100., true)); + // BBS + def = this->add("enable_wrapping_detection", coBool); + def->label = L("Enable wrapping detection"); + def->tooltip = L("Enable wrapping detection"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("wrapping_detection_layers", coInt); + def->label = L("Wrapping detection layers"); + def->tooltip = L("Wrapping detection layers."); + def->min = 0; + def->mode = comDevelop; + def->set_default_value(new ConfigOptionInt(20)); + + def = this->add("wrapping_detection_path", coPoints); + def->label = L("Wrapping detection position"); + def->tooltip = L("Wrapping detection position."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPoints{Vec2d(197.5, 326), Vec2d(207.5, 326)}); + def = this->add("sparse_infill_filament", coInt); def->gui_type = ConfigOptionDef::GUIType::i_enum_open; def->label = L("Infill"); @@ -3813,6 +3833,14 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionString()); + def = this->add("wrapping_detection_gcode", coString); + def->label = L("Wrapping detection G-code"); + def->multiline = true; + def->full_width = true; + def->height = 5; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionString("")); + def = this->add("silent_mode", coBool); def->label = L("Supports silent mode"); def->tooltip = L("Whether the machine supports silent mode in which machine use lower acceleration to print."); @@ -7738,7 +7766,11 @@ t_config_option_keys DynamicPrintConfig::normalize_fdm_2(int num_objects, int us ConfigOptionEnum* timelapse_opt = this->option>("timelapse_type"); bool is_smooth_timelapse = timelapse_opt != nullptr && timelapse_opt->value == TimelapseType::tlSmooth; - if (!is_smooth_timelapse && (used_filaments == 1 || (ps_opt->value == PrintSequence::ByObject && num_objects > 1))) { + + ConfigOptionBool *enable_wrapping_opt = this->option("enable_wrapping_detection"); + bool enable_wrapping = enable_wrapping_opt != nullptr && enable_wrapping_opt->value; + + if (!is_smooth_timelapse && !enable_wrapping && (used_filaments == 1 || (ps_opt->value == PrintSequence::ByObject && num_objects > 1))) { if (ept_opt->value) { ept_opt->value = false; changed_keys.push_back("enable_prime_tower"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 4c77f99801..e6217accda 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1257,6 +1257,9 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionIntsNullable, filament_flush_temp)) // BBS ((ConfigOptionBool, scan_first_layer)) + ((ConfigOptionBool, enable_wrapping_detection)) + ((ConfigOptionInt, wrapping_detection_layers)) + ((ConfigOptionPoints, wrapping_detection_path)) ((ConfigOptionPoints, thumbnail_size)) // ((ConfigOptionBool, spaghetti_detector)) ((ConfigOptionBool, gcode_add_line_number)) @@ -1266,6 +1269,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloat, time_cost)) ((ConfigOptionString, layer_change_gcode)) ((ConfigOptionString, time_lapse_gcode)) + ((ConfigOptionString, wrapping_detection_gcode)) ((ConfigOptionFloat, max_volumetric_extrusion_rate_slope)) ((ConfigOptionFloat, max_volumetric_extrusion_rate_slope_segment_length)) diff --git a/src/slic3r/GUI/DeviceManager.cpp b/src/slic3r/GUI/DeviceManager.cpp index 2cb5654225..60ef8667b6 100644 --- a/src/slic3r/GUI/DeviceManager.cpp +++ b/src/slic3r/GUI/DeviceManager.cpp @@ -5639,7 +5639,7 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ upgrade_display_hold_count = HOLD_COUNT_MAX; BOOST_LOG_TRIVIAL(info) << "ack of upgrade_confirm"; } - + if (j["upgrade"].contains("err_code")) { if (j["upgrade"]["err_code"].is_number()) { add_command_error_code_dlg(j["upgrade"]["err_code"].get()); @@ -7110,7 +7110,7 @@ void MachineObject::add_command_error_code_dlg(int command_err) if (!token.expired()) { m_command_error_code_dlgs.erase((GUI::DeviceErrorDialog*)event.GetEventObject());} event.Skip(); }); - + device_error_dialog->show_error_code(command_err); m_command_error_code_dlgs.insert(device_error_dialog); }); @@ -7912,6 +7912,11 @@ std::string DeviceManager::get_printer_ext_img(std::string type_str, int pos) { return std::string(); } +bool DeviceManager::support_wrapping_detection(const std::string &type_str) +{ + return get_value_from_config(type_str, "support_wrapping_deteciton"); +} + bool DeviceManager::get_printer_is_enclosed(std::string type_str) { return get_value_from_config(type_str, "printer_is_enclosed"); } diff --git a/src/slic3r/GUI/DeviceManager.hpp b/src/slic3r/GUI/DeviceManager.hpp index d01a0e2195..20b7fbd80e 100644 --- a/src/slic3r/GUI/DeviceManager.hpp +++ b/src/slic3r/GUI/DeviceManager.hpp @@ -1603,6 +1603,7 @@ public: static bool get_printer_is_enclosed(std::string type_str); static bool get_printer_can_set_nozzle(std::string type_str);// can set nozzle from studio static bool load_filaments_blacklist_config(); + static bool support_wrapping_detection(const std::string& type_str); static string get_fan_text(const std::string& type_str, const std::string& key); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 4810b20e9e..7c328d79d3 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2908,9 +2908,14 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re const DynamicPrintConfig &dconfig = wxGetApp().preset_bundle->prints.get_edited_preset().config; auto timelapse_type = dconfig.option>("timelapse_type"); - bool timelapse_enabled = timelapse_type ? (timelapse_type->value == TimelapseType::tlSmooth) : false; + bool need_wipe_tower = timelapse_type ? (timelapse_type->value == TimelapseType::tlSmooth) : false; - if (wt && (timelapse_enabled || filaments_count > 1) && !wxGetApp().plater()->only_gcode_mode() && !wxGetApp().plater()->is_gcode_3mf()) { + const DynamicPrintConfig & printer_config = wxGetApp().preset_bundle->printers.get_edited_preset().config; + if (printer_config.has("enable_wrapping_detection")) { + need_wipe_tower |= dynamic_cast(printer_config.option("enable_wrapping_detection"))->value; + } + + if (wt && (need_wipe_tower || filaments_count > 1) && !wxGetApp().plater()->only_gcode_mode() && !wxGetApp().plater()->is_gcode_3mf()) { for (int plate_id = 0; plate_id < n_plates; plate_id++) { // If print ByObject and there is only one object in the plate, the wipe tower is allowed to be generated. PartPlate* part_plate = ppl.get_plate(plate_id); @@ -2931,14 +2936,14 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re const Print* print = m_process->fff_print(); const Print* current_print = part_plate->fff_print(); - if (!timelapse_enabled && part_plate->get_extruders(true).size() < 2) continue; + if (!need_wipe_tower && part_plate->get_extruders(true).size() < 2) continue; if (part_plate->get_objects_on_this_plate().empty()) continue; float brim_width = print->wipe_tower_data(filaments_count).brim_width; const DynamicPrintConfig &print_cfg = wxGetApp().preset_bundle->prints.get_edited_preset().config; double wipe_vol = get_max_element(v); int nozzle_nums = wxGetApp().preset_bundle->get_printer_extruder_count(); - Vec3d wipe_tower_size = ppl.get_plate(plate_id)->estimate_wipe_tower_size(print_cfg, w, wipe_vol, nozzle_nums); + Vec3d wipe_tower_size = ppl.get_plate(plate_id)->estimate_wipe_tower_size(print_cfg, w, wipe_vol, nozzle_nums, 0, false, dynamic_cast(printer_config.option("enable_wrapping_detection"))->value); { const float margin = WIPE_TOWER_MARGIN + brim_width; diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 201e33c3af..df3489c636 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -1094,6 +1094,8 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config if (opt_key == "printable_area") ret = get_thumbnails_string(config.option(opt_key)->values); else if (opt_key == "bed_exclude_area") + ret = get_thumbnails_string(config.option(opt_key)->values); + else if (opt_key == "wrapping_detection_path") ret = get_thumbnails_string(config.option(opt_key)->values); else ret = config.option(opt_key)->get_at(idx); @@ -1230,6 +1232,8 @@ boost::any ConfigOptionsGroup::get_config_value2(const DynamicPrintConfig& confi ret = get_thumbnails_string(config.option(opt_key)->values); else if (opt_key == "bed_exclude_area") ret = get_thumbnails_string(config.option(opt_key)->values); + else if (opt_key == "wrapping_detection_path") + ret = get_thumbnails_string(config.option(opt_key)->values); else ret = config.option(opt_key)->get_at(idx); break; diff --git a/src/slic3r/GUI/PartPlate.cpp b/src/slic3r/GUI/PartPlate.cpp index 5254a0d63d..ecc30a6812 100644 --- a/src/slic3r/GUI/PartPlate.cpp +++ b/src/slic3r/GUI/PartPlate.cpp @@ -1905,7 +1905,7 @@ bool PartPlate::check_compatible_of_nozzle_and_filament(const DynamicPrintConfig return wipe_tower_size; }*/ -Vec3d PartPlate::estimate_wipe_tower_size(const DynamicPrintConfig & config, const double w, const double wipe_volume, int extruder_count, int plate_extruder_size, bool use_global_objects) const +Vec3d PartPlate::estimate_wipe_tower_size(const DynamicPrintConfig & config, const double w, const double wipe_volume, int extruder_count, int plate_extruder_size, bool use_global_objects, bool enable_wrapping_detection) const { Vec3d wipe_tower_size; double layer_height = 0.08f; // hard code layer height @@ -1935,7 +1935,7 @@ Vec3d PartPlate::estimate_wipe_tower_size(const DynamicPrintConfig & config, con wipe_tower_size(2) = max_height; //const DynamicPrintConfig &dconfig = wxGetApp().preset_bundle->prints.get_edited_preset().config; auto timelapse_type = config.option>("timelapse_type"); - bool timelapse_enabled = timelapse_type ? (timelapse_type->value == TimelapseType::tlSmooth) : false; + bool need_wipe_tower = (timelapse_type ? (timelapse_type->value == TimelapseType::tlSmooth) : false) | enable_wrapping_detection; double extra_spacing = config.option("prime_tower_infill_gap")->getFloat() / 100.; const ConfigOptionBool* use_rib_wall_opt = config.option("prime_tower_rib_wall"); bool use_rib_wall = use_rib_wall_opt ? use_rib_wall_opt->value: true; @@ -1958,7 +1958,7 @@ Vec3d PartPlate::estimate_wipe_tower_size(const DynamicPrintConfig & config, con if (extruder_count == 2) volume += filament_change_volume * (int) (plate_extruder_size / 2); if (use_rib_wall) { depth = std::sqrt(volume / layer_height * extra_spacing); - if (timelapse_enabled || plate_extruder_size > 1) { + if (need_wipe_tower || plate_extruder_size > 1) { float min_wipe_tower_depth = WipeTower::get_limit_depth_by_height(max_height); depth = std::max((double) min_wipe_tower_depth, depth); depth += rib_width / std::sqrt(2) + m_print->config().prime_tower_extra_rib_length.value; @@ -1967,7 +1967,7 @@ Vec3d PartPlate::estimate_wipe_tower_size(const DynamicPrintConfig & config, con } else { depth = volume/ (layer_height * w) *extra_spacing; - if (timelapse_enabled || depth > EPSILON) { + if (need_wipe_tower || depth > EPSILON) { float min_wipe_tower_depth = WipeTower::get_limit_depth_by_height(max_height); depth = std::max((double)min_wipe_tower_depth, depth); } @@ -1986,7 +1986,9 @@ arrangement::ArrangePolygon PartPlate::estimate_wipe_tower_polygon(const Dynamic //float a = dynamic_cast(config.option("wipe_tower_rotation_angle"))->value; std::vector v = dynamic_cast(config.option("filament_prime_volume"))->values; float tower_brim_width = dynamic_cast(config.option("prime_tower_brim_width"))->value; - wt_size = estimate_wipe_tower_size(config, w, get_max_element(v), extruder_count, plate_extruder_size, use_global_objects); + const ConfigOptionBool * wrapping_opt = dynamic_cast(config.option("enable_wrapping_detection")); + bool enable_wrapping = (wrapping_opt != nullptr) && wrapping_opt->value; + wt_size = estimate_wipe_tower_size(config, w, get_max_element(v), extruder_count, plate_extruder_size, use_global_objects, enable_wrapping); int plate_width=m_width, plate_depth=m_depth; float depth = wt_size(1); float margin = WIPE_TOWER_MARGIN + tower_brim_width, wp_brim_width = 0.f; diff --git a/src/slic3r/GUI/PartPlate.hpp b/src/slic3r/GUI/PartPlate.hpp index 3836eef6f7..b82d4219f5 100644 --- a/src/slic3r/GUI/PartPlate.hpp +++ b/src/slic3r/GUI/PartPlate.hpp @@ -322,7 +322,7 @@ public: Vec3d get_origin() { return m_origin; } //Vec3d calculate_wipe_tower_size(const DynamicPrintConfig &config, const double w, const double wipe_volume, int plate_extruder_size = 0, bool use_global_objects = false) const; - Vec3d estimate_wipe_tower_size(const DynamicPrintConfig & config, const double w, const double wipe_volume, int extruder_count = 1, int plate_extruder_size = 0, bool use_global_objects = false) const; + Vec3d estimate_wipe_tower_size(const DynamicPrintConfig & config, const double w, const double wipe_volume, int extruder_count = 1, int plate_extruder_size = 0, bool use_global_objects = false, bool enable_wrapping_detection = false) const; arrangement::ArrangePolygon estimate_wipe_tower_polygon(const DynamicPrintConfig & config, int plate_index, Vec3d& wt_pos, Vec3d& wt_size, int extruder_count = 1, int plate_extruder_size = 0, bool use_global_objects = false) const; bool check_objects_empty_and_gcode3mf(std::vector &result) const; // get used filaments from config, 1 based idx diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 25d6b7d0e7..80e2ff4e09 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -956,7 +956,8 @@ void Tab::init_options_list() for (const std::string& opt_key : m_config->keys()) { - if (opt_key == "printable_area" || opt_key == "bed_exclude_area" || opt_key == "compatible_prints" || opt_key == "compatible_printers" || opt_key == "thumbnails") { + if (opt_key == "printable_area" || opt_key == "bed_exclude_area" || opt_key == "compatible_prints" || opt_key == "compatible_printers" || opt_key == "thumbnails" + || opt_key == "wrapping_detection_path") { m_options_list.emplace(opt_key, m_opt_status_value); continue; } @@ -1569,13 +1570,31 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) if (opt_key == "enable_prime_tower") { auto timelapse_type = m_config->option>("timelapse_type"); bool timelapse_enabled = timelapse_type->value == TimelapseType::tlSmooth; - if (!boost::any_cast(value) && timelapse_enabled) { - MessageDialog dlg(wxGetApp().plater(), _L("A prime tower is required for smooth timelapse. There may be flaws on the model without prime tower. Are you sure you want to disable prime tower?"), - _L("Warning"), wxICON_WARNING | wxYES | wxNO); - if (dlg.ShowModal() == wxID_NO) { - DynamicPrintConfig new_conf = *m_config; - new_conf.set_key_value("enable_prime_tower", new ConfigOptionBool(true)); - m_config_manipulation.apply(m_config, &new_conf); + if (!boost::any_cast(value)) { + bool set_enable_prime_tower = false; + if (timelapse_enabled) { + MessageDialog + dlg(wxGetApp().plater(), + _L("A prime tower is required for smooth timelapse. There may be flaws on the model without prime tower. Are you sure you want to disable prime tower?"), + _L("Warning"), wxICON_WARNING | wxYES | wxNO); + if (dlg.ShowModal() == wxID_NO) { + DynamicPrintConfig new_conf = *m_config; + new_conf.set_key_value("enable_prime_tower", new ConfigOptionBool(true)); + m_config_manipulation.apply(m_config, &new_conf); + set_enable_prime_tower = true; + } + } + bool enable_wrapping = m_preset_bundle->printers.get_edited_preset().config.option("enable_wrapping_detection")->value; + if (enable_wrapping && !set_enable_prime_tower) { + MessageDialog dlg(wxGetApp().plater(), + _L("A prime tower is required for wrapping detection. There may be flaws on the model without prime tower. Are you sure you want to disable prime tower?"), + _L("Warning"), wxICON_WARNING | wxYES | wxNO); + if (dlg.ShowModal() == wxID_NO) { + DynamicPrintConfig new_conf = *m_config; + new_conf.set_key_value("enable_prime_tower", new ConfigOptionBool(true)); + m_config_manipulation.apply(m_config, &new_conf); + set_enable_prime_tower = true; + } } wxGetApp().plater()->update(); } @@ -1593,6 +1612,23 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) update_wiping_button_visibility(); } + if (opt_key == "enable_wrapping_detection") { + bool wipe_tower_enabled = m_preset_bundle->prints.get_edited_preset().config.option("enable_prime_tower")->value; + if (boost::any_cast(value) && !wipe_tower_enabled) { + MessageDialog dlg(wxGetApp().plater(), + _L("Prime tower is required for wrapping detection. There may be flaws on the model without prime tower. Do you still want to enable wrapping detection?"), + _L("Warning"), wxICON_WARNING | wxYES | wxNO); + if (dlg.ShowModal() == wxID_NO) { + DynamicPrintConfig new_conf = *m_config; + new_conf.set_key_value("enable_wrapping_detection", new ConfigOptionBool(false)); + m_config_manipulation.apply(m_config, &new_conf); + wxGetApp().plater()->update(); + } + } else { + wxGetApp().plater()->update(); + } + } + if (opt_key == "precise_z_height") { bool wipe_tower_enabled = m_config->option("enable_prime_tower")->value; if (boost::any_cast(value) && wipe_tower_enabled) { @@ -4162,6 +4198,9 @@ void TabPrinter::build_fff() optgroup->append_single_option_line("pellet_modded_printer", "pellet-flow-coefficient"); optgroup->append_single_option_line("bbl_use_printhost"); optgroup->append_single_option_line("scan_first_layer"); + optgroup->append_single_option_line("enable_wrapping_detection"); + optgroup->append_single_option_line("wrapping_detection_layers"); + //optgroup->append_single_option_line("wrapping_detection_path"); optgroup->append_single_option_line("disable_m73"); option = optgroup->get_option("thumbnails"); option.opt.full_width = true; @@ -4306,6 +4345,17 @@ void TabPrinter::build_fff() option.opt.height = gcode_field_height;//150; optgroup->append_single_option_line(option); + optgroup = page->new_optgroup(L("Wrapping Detection G-code"), L"param_gcode", 0); + optgroup->m_on_change = [this, optgroup](const t_config_option_key &opt_key, const boost::any &value) { + validate_custom_gcode_cb(this, optgroup, opt_key, value); + }; + optgroup->edit_custom_gcode = edit_custom_gcode_fn; + option = optgroup->get_option("wrapping_detection_gcode"); + option.opt.full_width = true; + option.opt.is_code = true; + option.opt.height = gcode_field_height; // 150; + optgroup->append_single_option_line(option); + optgroup = page->new_optgroup(L("Change filament G-code"), L"param_gcode", 0); optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { validate_custom_gcode_cb(this, optgroup_title, opt_key, value); @@ -4975,6 +5025,16 @@ void TabPrinter::toggle_options() // SoftFever: hide non-BBL settings for (auto el : {"use_firmware_retraction", "use_relative_e_distances", "support_multi_bed_types", "pellet_modded_printer", "bed_mesh_max", "bed_mesh_min", "bed_mesh_probe_distance", "adaptive_bed_mesh_margin", "thumbnails"}) toggle_line(el, !is_BBL_printer); + + PresetBundle *preset_bundle = wxGetApp().preset_bundle; + std::string printer_type = preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle); + toggle_line("enable_wrapping_detection", DeviceManager::support_wrapping_detection(printer_type)); + } + + if (m_active_page->title() == L("Machine G-code")) { + PresetBundle *preset_bundle = wxGetApp().preset_bundle; + std::string printer_type = preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle); + toggle_line("wrapping_detection_gcode", DeviceManager::support_wrapping_detection(printer_type)); } if (m_active_page->title() == L("Multimaterial")) { diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index d5dea0a645..9858b4c299 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -1363,6 +1363,9 @@ static wxString get_string_value(std::string opt_key, const DynamicPrintConfig& else if (opt_key == "head_wrap_detect_zone") { return get_thumbnails_string(config.option(opt_key)->values); } + else if (opt_key == "wrapping_detection_path") { + return get_thumbnails_string(config.option(opt_key)->values); + } Vec2d val = config.opt(opt_key)->get_at(opt_idx); return from_u8((boost::format("[%1%]") % ConfigOptionPoint(val).serialize()).str()); }