diff --git a/bbl/i18n/list.txt b/bbl/i18n/list.txt index 60f99e9654..7d2fa58444 100644 --- a/bbl/i18n/list.txt +++ b/bbl/i18n/list.txt @@ -21,6 +21,7 @@ src/slic3r/GUI/GUI_Init.cpp src/slic3r/GUI/GUI_Factories.cpp src/slic3r/GUI/GUI_ObjectList.cpp src/slic3r/GUI/GUI_ObjectSettings.cpp +src/slic3r/GUI/GUI_ObjectLayers.cpp src/slic3r/GUI/GUI_ObjectTable.cpp src/slic3r/GUI/GUI_ObjectTable.hpp src/slic3r/GUI/GUI_ObjectTableSettings.cpp diff --git a/resources/config.json b/resources/config.json index c979710858..e0a5acfa6b 100644 --- a/resources/config.json +++ b/resources/config.json @@ -20,33 +20,33 @@ "FUNC_PRINT_ALL" : false }, "camera_resolution":["720p"], - "bed_temperature_limit": 120, + "bed_temperature_limit": 100, "model_id": "C11", "printer_type": "C11", "ftp_folder" : "sdcard/", "printer_thumbnail_image": "printer_thumbnail_p1p" }, - { + { "display_name": "Bambu Lab X1", "func": { + "FUNC_VIRTUAL_TYAY" : true, "FUNC_EXTRUSION_CALI": false, "FUNC_LOCAL_TUNNEL": false }, - "camera_resolution":["720p","1080p"], - "bed_temperature_limit": 120, "model_id": "BL-P002", + "camera_resolution":["720p","1080p"], "printer_type": "3DPrinter-X1", "printer_thumbnail_image": "printer_thumbnail" }, { "display_name": "Bambu Lab X1 Carbon", "func": { + "FUNC_VIRTUAL_TYAY" : true, "FUNC_EXTRUSION_CALI": false, "FUNC_LOCAL_TUNNEL": false }, "model_id": "BL-P001", "camera_resolution":["720p","1080p"], - "bed_temperature_limit": 120, "printer_type": "3DPrinter-X1-Carbon", "printer_thumbnail_image": "printer_thumbnail" } diff --git a/resources/data/hints.ini b/resources/data/hints.ini index 28a38af4d0..03f88b0388 100644 --- a/resources/data/hints.ini +++ b/resources/data/hints.ini @@ -155,7 +155,7 @@ documentation_link= https://wiki.bambulab.com/en/software/bambu-studio/set-param [hint:Stack objects] text = Stack objects\nDid you know that you can stack objects as a whole one? -documentation_link= https://wiki.bambulab.com/e/en/software/bambu-studio/stacking-objects +documentation_link= https://wiki.bambulab.com/en/software/bambu-studio/stacking-objects [hint:Flush into support/objects/infill] text = Flush into support/objects/infill\nDid you know that you can save the wasted filament by flushing them into support/objects/infill during filament change? diff --git a/resources/images/ams_humidity_0.svg b/resources/images/ams_humidity_0.svg index 78eafd2efa..d438d503a9 100644 --- a/resources/images/ams_humidity_0.svg +++ b/resources/images/ams_humidity_0.svg @@ -1,11 +1,11 @@ - - - - - - - - - - + + + + + + + + + + diff --git a/resources/images/ams_humidity_1.svg b/resources/images/ams_humidity_1.svg index b5322212d3..caabcbdbe1 100644 --- a/resources/images/ams_humidity_1.svg +++ b/resources/images/ams_humidity_1.svg @@ -1,12 +1,12 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/resources/images/ams_humidity_2.svg b/resources/images/ams_humidity_2.svg index 8111ddbf7b..1ccea8ea3b 100644 --- a/resources/images/ams_humidity_2.svg +++ b/resources/images/ams_humidity_2.svg @@ -1,12 +1,12 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/resources/images/ams_humidity_3.svg b/resources/images/ams_humidity_3.svg index e2b16b3cb1..18cd50462e 100644 --- a/resources/images/ams_humidity_3.svg +++ b/resources/images/ams_humidity_3.svg @@ -1,12 +1,12 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/resources/images/ams_humidity_4.svg b/resources/images/ams_humidity_4.svg index 64383365c0..8d41debf74 100644 --- a/resources/images/ams_humidity_4.svg +++ b/resources/images/ams_humidity_4.svg @@ -1,12 +1,12 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/resources/images/ams_humidity_tips.svg b/resources/images/ams_humidity_tips.svg index 96055be7d7..b343abfd39 100644 --- a/resources/images/ams_humidity_tips.svg +++ b/resources/images/ams_humidity_tips.svg @@ -1,16 +1,16 @@ - - + + - - - - - + + + + + diff --git a/resources/images/automatic_material_renewal.svg b/resources/images/automatic_material_renewal.svg new file mode 100644 index 0000000000..a1138c5e6e --- /dev/null +++ b/resources/images/automatic_material_renewal.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/resources/images/collapse_btn.svg b/resources/images/collapse_btn.svg new file mode 100644 index 0000000000..32d7f99595 --- /dev/null +++ b/resources/images/collapse_btn.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/resources/images/color_picker_border.svg b/resources/images/color_picker_border.svg new file mode 100644 index 0000000000..1f0d10a858 --- /dev/null +++ b/resources/images/color_picker_border.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/cut_.svg b/resources/images/cut_.svg new file mode 100644 index 0000000000..0c238ef449 --- /dev/null +++ b/resources/images/cut_.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/images/cut_connectors.svg b/resources/images/cut_connectors.svg new file mode 100644 index 0000000000..61c3436c0d --- /dev/null +++ b/resources/images/cut_connectors.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/images/expand_btn.svg b/resources/images/expand_btn.svg new file mode 100644 index 0000000000..4ee221a44a --- /dev/null +++ b/resources/images/expand_btn.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/images/im_gcode_custom.svg b/resources/images/im_gcode_custom.svg new file mode 100644 index 0000000000..a844eb2542 --- /dev/null +++ b/resources/images/im_gcode_custom.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/images/monitor_recording_off.svg b/resources/images/monitor_recording_off.svg index fa08204bd5..c45f93e9a6 100644 --- a/resources/images/monitor_recording_off.svg +++ b/resources/images/monitor_recording_off.svg @@ -1,6 +1,6 @@ - - - - + + + + diff --git a/resources/images/monitor_recording_off_dark.svg b/resources/images/monitor_recording_off_dark.svg new file mode 100644 index 0000000000..520dd1bdb1 --- /dev/null +++ b/resources/images/monitor_recording_off_dark.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/resources/images/monitor_recording_on.svg b/resources/images/monitor_recording_on.svg index 33c348eb25..4bccded8bd 100644 --- a/resources/images/monitor_recording_on.svg +++ b/resources/images/monitor_recording_on.svg @@ -1,7 +1,7 @@ - + - - - + + + diff --git a/resources/images/monitor_recording_on_dark.svg b/resources/images/monitor_recording_on_dark.svg new file mode 100644 index 0000000000..cc0b3ec16f --- /dev/null +++ b/resources/images/monitor_recording_on_dark.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/resources/images/monitor_state_off.svg b/resources/images/monitor_state_off.svg deleted file mode 100644 index 9a6de7d082..0000000000 --- a/resources/images/monitor_state_off.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - Layer 1 - - - - - - \ No newline at end of file diff --git a/resources/images/monitor_timelapse_off.svg b/resources/images/monitor_timelapse_off.svg index 21d9fd69e6..103a97f3fc 100644 --- a/resources/images/monitor_timelapse_off.svg +++ b/resources/images/monitor_timelapse_off.svg @@ -1,11 +1,11 @@ - - - - - - + + + + + + diff --git a/resources/images/monitor_timelapse_off_dark.svg b/resources/images/monitor_timelapse_off_dark.svg new file mode 100644 index 0000000000..19785321b1 --- /dev/null +++ b/resources/images/monitor_timelapse_off_dark.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/resources/images/monitor_timelapse_on.svg b/resources/images/monitor_timelapse_on.svg index c82e3ca0a5..ca46179f76 100644 --- a/resources/images/monitor_timelapse_on.svg +++ b/resources/images/monitor_timelapse_on.svg @@ -1,12 +1,12 @@ - - + + - - - - + + + + diff --git a/resources/images/monitor_timelapse_on_dark.svg b/resources/images/monitor_timelapse_on_dark.svg new file mode 100644 index 0000000000..63ab2297c2 --- /dev/null +++ b/resources/images/monitor_timelapse_on_dark.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/images/monitor_vcamera_off.svg b/resources/images/monitor_vcamera_off.svg index 51609adc4a..edbcc92370 100644 --- a/resources/images/monitor_vcamera_off.svg +++ b/resources/images/monitor_vcamera_off.svg @@ -1,7 +1,7 @@ - - - - - + + + + + diff --git a/resources/images/monitor_vcamera_off_dark.svg b/resources/images/monitor_vcamera_off_dark.svg new file mode 100644 index 0000000000..8ed58c9da9 --- /dev/null +++ b/resources/images/monitor_vcamera_off_dark.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/resources/images/monitor_vcamera_on.svg b/resources/images/monitor_vcamera_on.svg index 5e741389eb..b26c3f10cf 100644 --- a/resources/images/monitor_vcamera_on.svg +++ b/resources/images/monitor_vcamera_on.svg @@ -1,8 +1,8 @@ - - - + + + - - + + diff --git a/resources/images/monitor_vcamera_on_dark.svg b/resources/images/monitor_vcamera_on_dark.svg new file mode 100644 index 0000000000..1c829d6554 --- /dev/null +++ b/resources/images/monitor_vcamera_on_dark.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/resources/images/objlist_sinking.svg b/resources/images/objlist_sinking.svg index c1309ee7b1..a5f948f442 100644 --- a/resources/images/objlist_sinking.svg +++ b/resources/images/objlist_sinking.svg @@ -1,4 +1,5 @@ - - - + + + + diff --git a/resources/images/param_monotonic.svg b/resources/images/param_monotonic.svg index 0c9245e28a..b959242708 100644 --- a/resources/images/param_monotonic.svg +++ b/resources/images/param_monotonic.svg @@ -1,14 +1,20 @@ - + + + + + + + - + - + diff --git a/resources/images/param_monotonicline.svg b/resources/images/param_monotonicline.svg index b959242708..0c9245e28a 100644 --- a/resources/images/param_monotonicline.svg +++ b/resources/images/param_monotonicline.svg @@ -1,20 +1,14 @@ - + - - - - - - - + - + diff --git a/resources/images/replace_arrow_down.svg b/resources/images/replace_arrow_down.svg new file mode 100644 index 0000000000..d46126623d --- /dev/null +++ b/resources/images/replace_arrow_down.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/replace_arrow_left.svg b/resources/images/replace_arrow_left.svg new file mode 100644 index 0000000000..0c95675d13 --- /dev/null +++ b/resources/images/replace_arrow_left.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/replace_arrow_right.svg b/resources/images/replace_arrow_right.svg new file mode 100644 index 0000000000..76bb1f417b --- /dev/null +++ b/resources/images/replace_arrow_right.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/revert_btn.svg b/resources/images/revert_btn.svg new file mode 100644 index 0000000000..fbc580d884 --- /dev/null +++ b/resources/images/revert_btn.svg @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/resources/images/sdcard_state_abnormal.svg b/resources/images/sdcard_state_abnormal.svg index b2ac57f52a..a88e8df175 100644 --- a/resources/images/sdcard_state_abnormal.svg +++ b/resources/images/sdcard_state_abnormal.svg @@ -1,9 +1,9 @@ - - - - - + + + + + - + diff --git a/resources/images/sdcard_state_abnormal_dark.svg b/resources/images/sdcard_state_abnormal_dark.svg new file mode 100644 index 0000000000..b2ac57f52a --- /dev/null +++ b/resources/images/sdcard_state_abnormal_dark.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/images/sdcard_state_no.svg b/resources/images/sdcard_state_no.svg index 6b17701408..ae45a0c3b5 100644 --- a/resources/images/sdcard_state_no.svg +++ b/resources/images/sdcard_state_no.svg @@ -1,10 +1,10 @@ - - - - - - - - + + + + + + + + diff --git a/resources/images/sdcard_state_no_dark.svg b/resources/images/sdcard_state_no_dark.svg new file mode 100644 index 0000000000..6b17701408 --- /dev/null +++ b/resources/images/sdcard_state_no_dark.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/resources/images/sdcard_state_normal.svg b/resources/images/sdcard_state_normal.svg index c9004ca461..2c55e87010 100644 --- a/resources/images/sdcard_state_normal.svg +++ b/resources/images/sdcard_state_normal.svg @@ -1,9 +1,9 @@ - - - - - - + + + + + + diff --git a/resources/images/sdcard_state_normal_dark.svg b/resources/images/sdcard_state_normal_dark.svg new file mode 100644 index 0000000000..09217c6d67 --- /dev/null +++ b/resources/images/sdcard_state_normal_dark.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/printers/filaments_blacklist.json b/resources/printers/filaments_blacklist.json index e5edcf9eb1..685f3cb536 100644 --- a/resources/printers/filaments_blacklist.json +++ b/resources/printers/filaments_blacklist.json @@ -7,12 +7,30 @@ "type": "TPU", "action": "prohibition", "description": "TPU is not supported by AMS." + }, + { + "vendor": "Bambulab", + "type": "TPU", + "action": "prohibition", + "description": "TPU is not supported by AMS." }, { "vendor": "Third Party", "type": "PVA", "action": "warning", "description": "Damp PVA will become flexible and get stuck inside AMS,please take care to dry it before use." + }, + { + "vendor": "Third Party", + "type": "PLA-CF", + "action": "warning", + "description": "CF/GF filaments are hard and brittle, It's easy to break or get stuck in AMS, please use with caution." + }, + { + "vendor": "Third Party", + "type": "PETG-CF", + "action": "warning", + "description": "CF/GF filaments are hard and brittle, It's easy to break or get stuck in AMS, please use with caution." }, { "vendor": "Third Party", @@ -22,9 +40,21 @@ }, { "vendor": "Third Party", - "type": "PLA-CF", + "type": "PAHT-CF", "action": "warning", "description": "CF/GF filaments are hard and brittle, It's easy to break or get stuck in AMS, please use with caution." + }, + { + "vendor": "Bambulab", + "type": "PET-CF", + "action": "prohibition", + "description": "Bambu PET-CF/PA6-CF is not supported by AMS." + }, + { + "vendor": "Bambulab", + "type": "PA6-CF", + "action": "prohibition", + "description": "Bambu PET-CF/PA6-CF is not supported by AMS." } ] } diff --git a/resources/profiles/Anycubic/machine/fdm_machine_common.json b/resources/profiles/Anycubic/machine/fdm_machine_common.json index 89cad31412..5ca37e5f16 100644 --- a/resources/profiles/Anycubic/machine/fdm_machine_common.json +++ b/resources/profiles/Anycubic/machine/fdm_machine_common.json @@ -131,7 +131,7 @@ "change_filament_gcode": "", "before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]\nG92 E0\n", "layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]", - "machine_pause_gcode": "M400 U1\n", + "machine_pause_gcode": "M400 U1", "wipe": [ "1" ], diff --git a/resources/profiles/BBL.json b/resources/profiles/BBL.json index e6e570e98b..fbfbf38e0b 100644 --- a/resources/profiles/BBL.json +++ b/resources/profiles/BBL.json @@ -1,7 +1,7 @@ { "name": "Bambulab", "url": "http://www.bambulab.com/Parameters/vendor/BBL.json", - "version": "01.06.00.02", + "version": "01.06.00.03", "force_update": "0", "description": "the initial version of BBL configurations", "machine_model_list": [ @@ -321,6 +321,14 @@ "name": "Bambu PLA Metal @base", "sub_path": "filament/Bambu PLA Metal @base.json" }, + { + "name": "Bambu PLA Silk @base", + "sub_path": "filament/Bambu PLA Silk @base.json" + }, + { + "name": "Bambu PETG Basic @base", + "sub_path": "filament/Bambu PETG Basic @base.json" + }, { "name": "Bambu ABS @base", "sub_path": "filament/Bambu ABS @base.json" @@ -541,6 +549,38 @@ "name": "Bambu PLA Sparkle @BBL X1C", "sub_path": "filament/Bambu PLA Sparkle @BBL X1C.json" }, + { + "name": "Bambu PLA Silk @BBL X1", + "sub_path": "filament/Bambu PLA Silk @BBL X1.json" + }, + { + "name": "Bambu PLA Silk @BBL X1C 0.2 nozzle", + "sub_path": "filament/Bambu PLA Silk @BBL X1C 0.2 nozzle.json" + }, + { + "name": "Bambu PLA Silk @BBL X1C", + "sub_path": "filament/Bambu PLA Silk @BBL X1C.json" + }, + { + "name": "Bambu PLA Silk @BBL P1P", + "sub_path": "filament/P1P/Bambu PLA Silk @BBL P1P.json" + }, + { + "name": "Bambu PLA Silk @BBL P1P 0.2 nozzle", + "sub_path": "filament/P1P/Bambu PLA Silk @BBL P1P 0.2 nozzle.json" + }, + { + "name": "Bambu PETG Basic @BBL X1C", + "sub_path": "filament/Bambu PETG Basic @BBL X1C.json" + }, + { + "name": "Bambu PETG Basic @BBL X1C 0.2 nozzle", + "sub_path": "filament/Bambu PETG Basic @BBL X1C 0.2 nozzle.json" + }, + { + "name": "Bambu PETG Basic @BBL X1C 0.8 nozzle", + "sub_path": "filament/Bambu PETG Basic @BBL X1C 0.8 nozzle.json" + }, { "name": "Bambu ABS @BBL X1C", "sub_path": "filament/Bambu ABS @BBL X1C.json" diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X1C 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X1C 0.2 nozzle.json new file mode 100644 index 0000000000..7805e7125d --- /dev/null +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X1C 0.2 nozzle.json @@ -0,0 +1,16 @@ +{ + "type": "filament", + "setting_id": "GFSG00_00", + "name": "Bambu PETG Basic @BBL X1C 0.2 nozzle", + "from": "system", + "instantiation": "true", + "inherits": "Bambu PETG Basic @base", + "filament_max_volumetric_speed": [ + "1" + ], + "compatible_printers": [ + "Bambu Lab X1 Carbon 0.2 nozzle", + "Bambu Lab X1 0.2 nozzle", + "Bambu Lab P1P 0.2 nozzle" + ] +} diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X1C 0.8 nozzle.json b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X1C 0.8 nozzle.json new file mode 100644 index 0000000000..02590dc33d --- /dev/null +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X1C 0.8 nozzle.json @@ -0,0 +1,25 @@ +{ + "type": "filament", + "setting_id": "GFSG00_02", + "name": "Bambu PETG Basic @BBL X1C 0.8 nozzle", + "from": "system", + "instantiation": "true", + "inherits": "Bambu PETG Basic @base", + "filament_max_volumetric_speed": [ + "16" + ], + "fan_max_speed": [ + "60" + ], + "fan_min_speed": [ + "20" + ], + "compatible_printers": [ + "Bambu Lab X1 Carbon 0.6 nozzle", + "Bambu Lab X1 0.6 nozzle", + "Bambu Lab P1P 0.6 nozzle", + "Bambu Lab X1 Carbon 0.8 nozzle", + "Bambu Lab X1 0.8 nozzle", + "Bambu Lab P1P 0.8 nozzle" + ] +} diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X1C.json b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X1C.json new file mode 100644 index 0000000000..a7b23cc1fa --- /dev/null +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @BBL X1C.json @@ -0,0 +1,16 @@ +{ + "type": "filament", + "setting_id": "GFSG00_01", + "name": "Bambu PETG Basic @BBL X1C", + "from": "system", + "instantiation": "true", + "inherits": "Bambu PETG Basic @base", + "filament_max_volumetric_speed": [ + "14" + ], + "compatible_printers": [ + "Bambu Lab X1 Carbon 0.4 nozzle", + "Bambu Lab X1 0.4 nozzle", + "Bambu Lab P1P 0.4 nozzle" + ] +} diff --git a/resources/profiles/BBL/filament/Bambu PETG Basic @base.json b/resources/profiles/BBL/filament/Bambu PETG Basic @base.json new file mode 100644 index 0000000000..c306561a38 --- /dev/null +++ b/resources/profiles/BBL/filament/Bambu PETG Basic @base.json @@ -0,0 +1,86 @@ +{ + "type": "filament", + "filament_id": "GFG00", + "name": "Bambu PETG Basic @base", + "from": "system", + "instantiation": "false", + "inherits": "fdm_filament_pet", + "filament_flow_ratio": [ + "0.95" + ], + "temperature_vitrification": [ + "70" + ], + "overhang_fan_speed": [ + "90" + ], + "overhang_fan_threshold": [ + "25%" + ], + "fan_max_speed": [ + "40" + ], + "fan_min_speed": [ + "10" + ], + "filament_max_volumetric_speed": [ + "8" + ], + "slow_down_layer_time": [ + "8" + ], + "fan_cooling_layer_time": [ + "30" + ], + "cool_plate_temp" : [ + "0" + ], + "eng_plate_temp" : [ + "70" + ], + "hot_plate_temp" : [ + "70" + ], + "textured_plate_temp" : [ + "70" + ], + "cool_plate_temp_initial_layer" : [ + "0" + ], + "eng_plate_temp_initial_layer" : [ + "70" + ], + "hot_plate_temp_initial_layer" : [ + "70" + ], + "textured_plate_temp_initial_layer" : [ + "70" + ], + "filament_vendor": [ + "Bambu Lab" + ], + "filament_type": [ + "PETG" + ], + "filament_cost": [ + "24.99" + ], + "filament_density": [ + "1.25" + ], + "nozzle_temperature_range_low": [ + "240" + ], + "nozzle_temperature_range_high": [ + "270" + ], + "nozzle_temperature_initial_layer": [ + "255" + ], + "nozzle_temperature": [ + "255" + ], + "filament_start_gcode": [ + "; filament start gcode\n{if (bed_temperature[current_extruder] >60)||(bed_temperature_initial_layer[current_extruder] >60)}M106 P3 S255\n{elsif (bed_temperature[current_extruder] >45)||(bed_temperature_initial_layer[current_extruder] >45)}M106 P3 S180\n{endif}" + ] +} diff --git a/resources/profiles/BBL/filament/Bambu PETG-CF @base.json b/resources/profiles/BBL/filament/Bambu PETG-CF @base.json index d037da45d4..f6e26c2edb 100644 --- a/resources/profiles/BBL/filament/Bambu PETG-CF @base.json +++ b/resources/profiles/BBL/filament/Bambu PETG-CF @base.json @@ -5,6 +5,9 @@ "from": "system", "instantiation": "false", "inherits": "fdm_filament_pet", + "filament_flow_ratio": [ + "0.95" + ], "temperature_vitrification": [ "75" ], diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk @BBL X1.json b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL X1.json new file mode 100644 index 0000000000..47e74d129a --- /dev/null +++ b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL X1.json @@ -0,0 +1,19 @@ +{ + "type": "filament", + "setting_id": "GFSA05_02", + "name": "Bambu PLA Silk @BBL X1", + "from": "system", + "instantiation": "true", + "inherits": "Bambu PLA Silk @base", + "filament_max_volumetric_speed": [ + "12" + ], + "slow_down_layer_time": [ + "8" + ], + "compatible_printers": [ + "Bambu Lab X1 0.4 nozzle", + "Bambu Lab X1 0.6 nozzle", + "Bambu Lab X1 0.8 nozzle" + ] +} \ No newline at end of file diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk @BBL X1C 0.2 nozzle.json b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL X1C 0.2 nozzle.json new file mode 100644 index 0000000000..4a79c4245f --- /dev/null +++ b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL X1C 0.2 nozzle.json @@ -0,0 +1,15 @@ +{ + "type": "filament", + "setting_id": "GFSA05_00", + "name": "Bambu PLA Silk @BBL X1C 0.2 nozzle", + "from": "system", + "instantiation": "true", + "inherits": "Bambu PLA Silk @base", + "filament_max_volumetric_speed": [ + "2" + ], + "compatible_printers": [ + "Bambu Lab X1 Carbon 0.2 nozzle", + "Bambu Lab X1 0.2 nozzle" + ] +} \ No newline at end of file diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk @BBL X1C.json b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL X1C.json new file mode 100644 index 0000000000..f1f4151e0e --- /dev/null +++ b/resources/profiles/BBL/filament/Bambu PLA Silk @BBL X1C.json @@ -0,0 +1,16 @@ +{ + "type": "filament", + "setting_id": "GFSA05_01", + "name": "Bambu PLA Silk @BBL X1C", + "from": "system", + "instantiation": "true", + "inherits": "Bambu PLA Silk @base", + "filament_max_volumetric_speed": [ + "12" + ], + "compatible_printers": [ + "Bambu Lab X1 Carbon 0.4 nozzle", + "Bambu Lab X1 Carbon 0.6 nozzle", + "Bambu Lab X1 Carbon 0.8 nozzle" + ] +} \ No newline at end of file diff --git a/resources/profiles/BBL/filament/Bambu PLA Silk @base.json b/resources/profiles/BBL/filament/Bambu PLA Silk @base.json new file mode 100644 index 0000000000..888799234a --- /dev/null +++ b/resources/profiles/BBL/filament/Bambu PLA Silk @base.json @@ -0,0 +1,29 @@ +{ + "type": "filament", + "filament_id": "GFA05", + "name": "Bambu PLA Silk @base", + "from": "system", + "instantiation": "false", + "inherits": "fdm_filament_pla", + "filament_vendor": [ + "Bambu Lab" + ], + "required_nozzle_HRC": [ + "3" + ], + "filament_flow_ratio": [ + "0.98" + ], + "filament_density": [ + "1.32" + ], + "filament_cost": [ + "29.99" + ], + "nozzle_temperature_initial_layer": [ + "230" + ], + "nozzle_temperature": [ + "230" + ] +} \ No newline at end of file diff --git a/resources/profiles/BBL/filament/Bambu PLA-CF @base.json b/resources/profiles/BBL/filament/Bambu PLA-CF @base.json index c9d8349c4d..533e5d6117 100644 --- a/resources/profiles/BBL/filament/Bambu PLA-CF @base.json +++ b/resources/profiles/BBL/filament/Bambu PLA-CF @base.json @@ -8,6 +8,12 @@ "filament_vendor": [ "Bambu Lab" ], + "cool_plate_temp" : [ + "45" + ], + "cool_plate_temp_initial_layer" : [ + "45" + ], "filament_cost": [ "34.99" ], diff --git a/resources/profiles/BBL/filament/Generic PETG-CF @base.json b/resources/profiles/BBL/filament/Generic PETG-CF @base.json index 69538b3ae9..949c11f5e2 100644 --- a/resources/profiles/BBL/filament/Generic PETG-CF @base.json +++ b/resources/profiles/BBL/filament/Generic PETG-CF @base.json @@ -5,6 +5,9 @@ "from": "system", "instantiation": "false", "inherits": "fdm_filament_pet", + "filament_flow_ratio": [ + "0.95" + ], "temperature_vitrification": [ "75" ], diff --git a/resources/profiles/BBL/filament/Generic PLA-CF @base.json b/resources/profiles/BBL/filament/Generic PLA-CF @base.json index df8db5d345..aebf40321e 100644 --- a/resources/profiles/BBL/filament/Generic PLA-CF @base.json +++ b/resources/profiles/BBL/filament/Generic PLA-CF @base.json @@ -8,6 +8,12 @@ "required_nozzle_HRC": [ "40" ], + "cool_plate_temp" : [ + "45" + ], + "cool_plate_temp_initial_layer" : [ + "45" + ], "filament_flow_ratio": [ "0.95" ], diff --git a/resources/profiles/BBL/filament/P1P/Bambu PLA Silk @BBL P1P 0.2 nozzle.json b/resources/profiles/BBL/filament/P1P/Bambu PLA Silk @BBL P1P 0.2 nozzle.json new file mode 100644 index 0000000000..a2c70f94de --- /dev/null +++ b/resources/profiles/BBL/filament/P1P/Bambu PLA Silk @BBL P1P 0.2 nozzle.json @@ -0,0 +1,26 @@ +{ + "type": "filament", + "setting_id": "GFSA05_11", + "name": "Bambu PLA Silk @BBL P1P 0.2 nozzle", + "from": "system", + "instantiation": "true", + "inherits": "Bambu PLA Silk @base", + "filament_max_volumetric_speed": [ + "2" + ], + "hot_plate_temp" : [ + "65" + ], + "hot_plate_temp_initial_layer" : [ + "65" + ], + "textured_plate_temp" : [ + "65" + ], + "textured_plate_temp_initial_layer" : [ + "65" + ], + "compatible_printers": [ + "Bambu Lab P1P 0.2 nozzle" + ] +} \ No newline at end of file diff --git a/resources/profiles/BBL/filament/P1P/Bambu PLA Silk @BBL P1P.json b/resources/profiles/BBL/filament/P1P/Bambu PLA Silk @BBL P1P.json new file mode 100644 index 0000000000..7e473c2f08 --- /dev/null +++ b/resources/profiles/BBL/filament/P1P/Bambu PLA Silk @BBL P1P.json @@ -0,0 +1,31 @@ +{ + "type": "filament", + "setting_id": "GFSA05_12", + "name": "Bambu PLA Silk @BBL P1P", + "from": "system", + "instantiation": "true", + "inherits": "Bambu PLA Silk @base", + "filament_max_volumetric_speed": [ + "12" + ], + "slow_down_layer_time": [ + "8" + ], + "hot_plate_temp" : [ + "65" + ], + "hot_plate_temp_initial_layer" : [ + "65" + ], + "textured_plate_temp" : [ + "65" + ], + "textured_plate_temp_initial_layer" : [ + "65" + ], + "compatible_printers": [ + "Bambu Lab P1P 0.4 nozzle", + "Bambu Lab P1P 0.6 nozzle", + "Bambu Lab P1P 0.8 nozzle" + ] +} \ No newline at end of file diff --git a/resources/profiles/BBL/filament/fdm_filament_common.json b/resources/profiles/BBL/filament/fdm_filament_common.json index 361fd9a9a9..646ec19711 100644 --- a/resources/profiles/BBL/filament/fdm_filament_common.json +++ b/resources/profiles/BBL/filament/fdm_filament_common.json @@ -3,6 +3,9 @@ "name": "fdm_filament_common", "from": "system", "instantiation": "false", + "filament_is_support": [ + "0" + ], "required_nozzle_HRC": [ "3" ], @@ -114,9 +117,6 @@ "filament_wipe_distance": [ "nil" ], - "bed_type": [ - "Cool Plate" - ], "nozzle_temperature_initial_layer": [ "200" ], diff --git a/resources/profiles/BBL/filament/fdm_filament_pva.json b/resources/profiles/BBL/filament/fdm_filament_pva.json index fb84ccf0a4..a6bfb295e2 100644 --- a/resources/profiles/BBL/filament/fdm_filament_pva.json +++ b/resources/profiles/BBL/filament/fdm_filament_pva.json @@ -5,7 +5,7 @@ "instantiation": "false", "inherits": "fdm_filament_common", "cool_plate_temp" : [ - "35" + "45" ], "eng_plate_temp" : [ "0" @@ -17,7 +17,7 @@ "55" ], "cool_plate_temp_initial_layer" : [ - "35" + "45" ], "eng_plate_temp_initial_layer" : [ "0" diff --git a/resources/profiles/BBL/machine/Bambu Lab P1P 0.2 nozzle.json b/resources/profiles/BBL/machine/Bambu Lab P1P 0.2 nozzle.json index 9f74e57285..380f00d7f5 100644 --- a/resources/profiles/BBL/machine/Bambu Lab P1P 0.2 nozzle.json +++ b/resources/profiles/BBL/machine/Bambu Lab P1P 0.2 nozzle.json @@ -26,7 +26,7 @@ "retraction_minimum_travel": [ "5" ], - "machine_start_gcode": ";===== machine: P1P ========================\n;===== date: 202230129 =====================\n;===== reset machine status =================\nG91\nM17 Z0.4 ; lower the z-motor current\nG0 Z12 F300 ; lower the hotbed , to prevent the nozzle is below the hotbed\nG0 Z-6;\nG90\nM17 X1.2 Y1.2 Z0.75 ; reset motor current to default\nM960 S5 P1 ; turn on logo lamp\nG90\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\nM73.2 R1.0 ;Reset left time magnitude\nM1002 set_gcode_claim_speed_level : 5\nM221 X0 Y0 Z0 ; turn off soft endstop to prevent protential logic problem\nG29.1 Z{+0.0} ; clear z-trim value first\n\n;===== heatbed preheat ====================\nM1002 gcode_claim_action : 2\nM140 S[bed_temperature_initial_layer_single] ;set bed temp\nM190 S[bed_temperature_initial_layer_single] ;wait for bed temp\n\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_tool]==\"PLA\"}\n {if (bed_temperature[current_extruder] >45)||(bed_temperature_initial_layer[current_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[current_extruder] >50)||(bed_temperature_initial_layer[current_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n;===== prepare print temperature and material ==========\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp\nG91\nG0 Z10 F1200\nG90\nG28 X\nM975 S1 ; turn on \nG1 X60 F12000\nG1 Y245\nG1 Y265 F3000\nM620 M\nM620 S[initial_tool]A ; switch material if AMS exist\n M109 S[nozzle_temperature_initial_layer]\n G1 X120 F12000\n\n G1 X20 Y50 F12000\n G1 Y-3\n T[initial_tool]\n G1 X54 F12000\n G1 Y265\n M400\nM621 S[initial_tool]A\n\nM412 S1 ; ===turn on filament runout detection===\n\nM109 S250 ;set nozzle to common flush temp\nM106 P1 S0\nG92 E0\nG1 E50 F200\nM400\nM104 S[nozzle_temperature_initial_layer]\nG92 E0\nG1 E50 F200\nM400\nM106 P1 S255\nG92 E0\nG1 E5 F300\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20} ; drop nozzle temp, make filament shink a bit\nG92 E0\nG1 E-0.5 F300\n\nG1 X70 F9000\nG1 X76 F15000\nG1 X65 F15000\nG1 X76 F15000\nG1 X65 F15000; shake to put down garbage\nG1 X80 F6000\nG1 X95 F15000\nG1 X80 F15000\nG1 X165 F15000; wipe and shake\nM400\nM106 P1 S0\n;===== prepare print temperature and material end =====\n\n\n;===== wipe nozzle ===============================\nM1002 gcode_claim_action : 14\nM975 S1\nM106 S255\nG1 X65 Y230 F18000\nG1 Y264 F6000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20}\nG1 X100 F18000 ; first wipe mouth\n\nG0 X135 Y253 F20000 ; move to exposed steel surface edge\nG28 Z P0 T300; home z with low precision,permit 300deg temperature\nG29.2 S0 ; turn off ABL\nG0 Z5 F20000\n\nG1 X60 Y265\nG92 E0\nG1 E-0.5 F300 ; retrack more\nG1 X100 F5000; second wipe mouth\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X90 F5000\nG0 X128 Y261 Z-1.5 F20000 ; move to exposed steel surface and stop the nozzle\nM104 S140 ; set temp down to heatbed acceptable\nM106 S255 ; turn on fan (G28 has turn off fan)\n\nM221 S; push soft endstop status\nM221 Z0 ;turn off Z axis endstop\nG0 Z0.5 F20000\nG0 X125 Y259.5 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 X128\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\n\nM109 S140 ; wait nozzle temp down to heatbed acceptable\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\n\nM221 R; pop softend status\nG1 Z10 F1200\nM400\nG1 Z10\nG1 F30000\nG1 X230 Y15\nG29.2 S1 ; turn on ABL\n;G28 ; home again after hard wipe mouth\nM106 S0 ; turn off fan , too noisy\n;===== wipe nozzle end ================================\n\n\n;===== bed leveling ==================================\nM1002 judge_flag g29_before_print_flag\nM622 J1\n\n M1002 gcode_claim_action : 1\n G29 A X{first_layer_print_min[0]} Y{first_layer_print_min[1]} I{first_layer_print_size[0]} J{first_layer_print_size[1]}\n M400\n M500 ; save cali data\n\nM623\n;===== bed leveling end ================================\n\n;===== home after wipe mouth============================\nM1002 judge_flag g29_before_print_flag\nM622 J0\n\n M1002 gcode_claim_action : 13\n G28\n\nM623\n;===== home after wipe mouth end =======================\n\nM975 S1 ; turn on vibration supression\n\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_tool]==\"PLA\"}\n {if (bed_temperature[current_extruder] >45)||(bed_temperature_initial_layer[current_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[current_extruder] >50)||(bed_temperature_initial_layer[current_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n\nM104 S{nozzle_temperature_initial_layer[initial_extruder]} ; set extrude temp earlier, to reduce wait time\n\n;===== mech mode fast check============================\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q1 A7 B30 C80 H15 K0\nM974 Q1 S2 P0\n\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q0 A7 B30 C90 Q0 H15 K0\nM974 Q0 S2 P0\n\nM975 S1\nG1 F30000\nG1 X230 Y15\nG28 X ; re-home XY \n;===== fmech mode fast check============================\n\n\n;===== noozle load line ===============================\nM975 S1\nG90 \nM83\nT1000\nG1 X18.0 Y1.0 Z0.8 F18000;Move to start position\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG1 Z0.2\nG0 E2 F300\nG0 X240 E15 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \nG0 Y11 E0.700 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\nG0 X239.5\nG0 E0.2\nG0 Y1.5 E0.700\nG0 X18 E15 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \nM400\n\n;===== for Textured PEI Plate , lower the nozzle as the nozzle was touching topmost of the texture when homing ==\n;curr_bed_type={curr_bed_type}\n{if curr_bed_type==\"Textured PEI Plate\"}\nG29.1 Z-0.04 ; for Textured PEI Plate\n{endif}\n;========turn off light and wait extrude temperature =============\nM1002 gcode_claim_action : 0\nM106 S0 ; turn off fan\nM106 P2 S0 ; turn off big fan \nM106 P3 S0 ; turn off chamber fan\n\nM975 S1 ; turn on mech mode supression", + "machine_start_gcode": ";===== machine: P1P ========================\n;===== date: 20230323 =====================\n;===== turn on the HB fan =================\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp to turn on the HB fan\n;===== reset machine status =================\nG91\nM17 Z0.4 ; lower the z-motor current\nG0 Z12 F300 ; lower the hotbed , to prevent the nozzle is below the hotbed\nG0 Z-6;\nG90\nM17 X1.2 Y1.2 Z0.75 ; reset motor current to default\nM960 S5 P1 ; turn on logo lamp\nG90\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\nM73.2 R1.0 ;Reset left time magnitude\nM1002 set_gcode_claim_speed_level : 5\nM221 X0 Y0 Z0 ; turn off soft endstop to prevent protential logic problem\nG29.1 Z{+0.0} ; clear z-trim value first\n\n;===== heatbed preheat ====================\nM1002 gcode_claim_action : 2\nM140 S[bed_temperature_initial_layer_single] ;set bed temp\nM190 S[bed_temperature_initial_layer_single] ;wait for bed temp\n\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_extruder]==\"PLA\"}\n {if (bed_temperature[initial_extruder] >45)||(bed_temperature_initial_layer[initial_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[initial_extruder] >50)||(bed_temperature_initial_layer[initial_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n;===== prepare print temperature and material ==========\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp\nG91\nG0 Z10 F1200\nG90\nG28 X\nM975 S1 ; turn on\nG1 X60 F12000\nG1 Y245\nG1 Y265 F3000\nM620 M\nM620 S[initial_extruder]A ; switch material if AMS exist\n M109 S[nozzle_temperature_initial_layer]\n G1 X120 F12000\n\n G1 X20 Y50 F12000\n G1 Y-3\n T[initial_extruder]\n G1 X54 F12000\n G1 Y265\n M400\nM621 S[initial_extruder]A\nM620.1 E F{filament_max_volumetric_speed[initial_extruder]/2.4053*60} T{nozzle_temperature_range_high[initial_extruder]}\n\n\nM412 S1 ; ===turn on filament runout detection===\n\nM109 S250 ;set nozzle to common flush temp\nM106 P1 S0\nG92 E0\nG1 E50 F200\nM400\nM104 S[nozzle_temperature_initial_layer]\nG92 E0\nG1 E50 F200\nM400\nM106 P1 S255\nG92 E0\nG1 E5 F300\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20} ; drop nozzle temp, make filament shink a bit\nG92 E0\nG1 E-0.5 F300\n\nG1 X70 F9000\nG1 X76 F15000\nG1 X65 F15000\nG1 X76 F15000\nG1 X65 F15000; shake to put down garbage\nG1 X80 F6000\nG1 X95 F15000\nG1 X80 F15000\nG1 X165 F15000; wipe and shake\nM400\nM106 P1 S0\n;===== prepare print temperature and material end =====\n\n\n;===== wipe nozzle ===============================\nM1002 gcode_claim_action : 14\nM975 S1\nM106 S255\nG1 X65 Y230 F18000\nG1 Y264 F6000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20}\nG1 X100 F18000 ; first wipe mouth\n\nG0 X135 Y253 F20000 ; move to exposed steel surface edge\nG28 Z P0 T300; home z with low precision,permit 300deg temperature\nG29.2 S0 ; turn off ABL\nG0 Z5 F20000\n\nG1 X60 Y265\nG92 E0\nG1 E-0.5 F300 ; retrack more\nG1 X100 F5000; second wipe mouth\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X90 F5000\nG0 X128 Y261 Z-1.5 F20000 ; move to exposed steel surface and stop the nozzle\nM104 S140 ; set temp down to heatbed acceptable\nM106 S255 ; turn on fan (G28 has turn off fan)\n\nM221 S; push soft endstop status\nM221 Z0 ;turn off Z axis endstop\nG0 Z0.5 F20000\nG0 X125 Y259.5 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 X128\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\n\nM109 S140 ; wait nozzle temp down to heatbed acceptable\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\n\nM221 R; pop softend status\nG1 Z10 F1200\nM400\nG1 Z10\nG1 F30000\nG1 X230 Y15\nG29.2 S1 ; turn on ABL\n;G28 ; home again after hard wipe mouth\nM106 S0 ; turn off fan , too noisy\n;===== wipe nozzle end ================================\n\n\n;===== bed leveling ==================================\nM1002 judge_flag g29_before_print_flag\nM622 J1\n\n M1002 gcode_claim_action : 1\n G29 A X{first_layer_print_min[0]} Y{first_layer_print_min[1]} I{first_layer_print_size[0]} J{first_layer_print_size[1]}\n M400\n M500 ; save cali data\n\nM623\n;===== bed leveling end ================================\n\n;===== home after wipe mouth============================\nM1002 judge_flag g29_before_print_flag\nM622 J0\n\n M1002 gcode_claim_action : 13\n G28\n\nM623\n;===== home after wipe mouth end =======================\n\nM975 S1 ; turn on vibration supression\n\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_extruder]==\"PLA\"}\n {if (bed_temperature[initial_extruder] >45)||(bed_temperature_initial_layer[initial_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[initial_extruder] >50)||(bed_temperature_initial_layer[initial_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n\nM104 S{nozzle_temperature_initial_layer[initial_extruder]} ; set extrude temp earlier, to reduce wait time\n\n;===== mech mode fast check============================\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q1 A7 B30 C80 H15 K0\nM974 Q1 S2 P0\n\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q0 A7 B30 C90 Q0 H15 K0\nM974 Q0 S2 P0\n\nM975 S1\nG1 F30000\nG1 X230 Y15\nG28 X ; re-home XY\n;===== fmech mode fast check============================\n\n\n;===== noozle load line ===============================\nM975 S1\nG90\nM83\nT1000\nG1 X18.0 Y1.0 Z0.8 F18000;Move to start position\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG1 Z0.2\nG0 E2 F300\nG0 X240 E15 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\nG0 Y11 E0.700 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\nG0 X239.5\nG0 E0.2\nG0 Y1.5 E0.700\nG0 X18 E15 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\nM400\n\n;===== for Textured PEI Plate , lower the nozzle as the nozzle was touching topmost of the texture when homing ==\n;curr_bed_type={curr_bed_type}\n{if curr_bed_type==\"Textured PEI Plate\"}\nG29.1 Z-0.04 ; for Textured PEI Plate\n{endif}\n;========turn off light and wait extrude temperature =============\nM1002 gcode_claim_action : 0\nM106 S0 ; turn off fan\nM106 P2 S0 ; turn off big fan\nM106 P3 S0 ; turn off chamber fan\n\nM975 S1 ; turn on mech mode supression", "upward_compatible_machine":[ "Bambu Lab X1 0.2 nozzle", "Bambu Lab X1 Carbon 0.2 nozzle" diff --git a/resources/profiles/BBL/machine/Bambu Lab P1P 0.4 nozzle.json b/resources/profiles/BBL/machine/Bambu Lab P1P 0.4 nozzle.json index 9c645e1268..be49085835 100644 --- a/resources/profiles/BBL/machine/Bambu Lab P1P 0.4 nozzle.json +++ b/resources/profiles/BBL/machine/Bambu Lab P1P 0.4 nozzle.json @@ -22,15 +22,15 @@ "18x28", "0x28" ], - "extruder_clearance_radius": "56", - "machine_pause_gcode": "M400 U1\n", - "machine_start_gcode": ";===== machine: P1P ========================\n;===== date: 202230129 =====================\n;===== reset machine status =================\nG91\nM17 Z0.4 ; lower the z-motor current\nG0 Z12 F300 ; lower the hotbed , to prevent the nozzle is below the hotbed\nG0 Z-6;\nG90\nM17 X1.2 Y1.2 Z0.75 ; reset motor current to default\nM960 S5 P1 ; turn on logo lamp\nG90\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\nM73.2 R1.0 ;Reset left time magnitude\nM1002 set_gcode_claim_speed_level : 5\nM221 X0 Y0 Z0 ; turn off soft endstop to prevent protential logic problem\nG29.1 Z{+0.0} ; clear z-trim value first\n\n;===== heatbed preheat ====================\nM1002 gcode_claim_action : 2\nM140 S[bed_temperature_initial_layer_single] ;set bed temp\nM190 S[bed_temperature_initial_layer_single] ;wait for bed temp\n\n\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_tool]==\"PLA\"}\n {if (bed_temperature[current_extruder] >45)||(bed_temperature_initial_layer[current_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[current_extruder] >50)||(bed_temperature_initial_layer[current_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n;===== prepare print temperature and material ==========\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp\nG91\nG0 Z10 F1200\nG90\nG28 X\nM975 S1 ; turn on \nG1 X60 F12000\nG1 Y245\nG1 Y265 F3000\nM620 M\nM620 S[initial_tool]A ; switch material if AMS exist\n M109 S[nozzle_temperature_initial_layer]\n G1 X120 F12000\n\n G1 X20 Y50 F12000\n G1 Y-3\n T[initial_tool]\n G1 X54 F12000\n G1 Y265\n M400\nM621 S[initial_tool]A\n\nM412 S1 ; ===turn on filament runout detection===\n\nM109 S250 ;set nozzle to common flush temp\nM106 P1 S0\nG92 E0\nG1 E50 F200\nM400\nM104 S[nozzle_temperature_initial_layer]\nG92 E0\nG1 E50 F200\nM400\nM106 P1 S255\nG92 E0\nG1 E5 F300\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20} ; drop nozzle temp, make filament shink a bit\nG92 E0\nG1 E-0.5 F300\n\nG1 X70 F9000\nG1 X76 F15000\nG1 X65 F15000\nG1 X76 F15000\nG1 X65 F15000; shake to put down garbage\nG1 X80 F6000\nG1 X95 F15000\nG1 X80 F15000\nG1 X165 F15000; wipe and shake\nM400\nM106 P1 S0\n;===== prepare print temperature and material end =====\n\n\n;===== wipe nozzle ===============================\nM1002 gcode_claim_action : 14\nM975 S1\nM106 S255\nG1 X65 Y230 F18000\nG1 Y264 F6000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20}\nG1 X100 F18000 ; first wipe mouth\n\nG0 X135 Y253 F20000 ; move to exposed steel surface edge\nG28 Z P0 T300; home z with low precision,permit 300deg temperature\nG29.2 S0 ; turn off ABL\nG0 Z5 F20000\n\nG1 X60 Y265\nG92 E0\nG1 E-0.5 F300 ; retrack more\nG1 X100 F5000; second wipe mouth\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X90 F5000\nG0 X128 Y261 Z-1.5 F20000 ; move to exposed steel surface and stop the nozzle\nM104 S140 ; set temp down to heatbed acceptable\nM106 S255 ; turn on fan (G28 has turn off fan)\n\nM221 S; push soft endstop status\nM221 Z0 ;turn off Z axis endstop\nG0 Z0.5 F20000\nG0 X125 Y259.5 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 X128\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\n\nM109 S140 ; wait nozzle temp down to heatbed acceptable\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\n\nM221 R; pop softend status\nG1 Z10 F1200\nM400\nG1 Z10\nG1 F30000\nG1 X230 Y15\nG29.2 S1 ; turn on ABL\n;G28 ; home again after hard wipe mouth\nM106 S0 ; turn off fan , too noisy\n;===== wipe nozzle end ================================\n\n\n;===== bed leveling ==================================\nM1002 judge_flag g29_before_print_flag\nM622 J1\n\n M1002 gcode_claim_action : 1\n G29 A X{first_layer_print_min[0]} Y{first_layer_print_min[1]} I{first_layer_print_size[0]} J{first_layer_print_size[1]}\n M400\n M500 ; save cali data\n\nM623\n;===== bed leveling end ================================\n\n;===== home after wipe mouth============================\nM1002 judge_flag g29_before_print_flag\nM622 J0\n\n M1002 gcode_claim_action : 13\n G28\n\nM623\n;===== home after wipe mouth end =======================\n\nM975 S1 ; turn on vibration supression\n\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_tool]==\"PLA\"}\n {if (bed_temperature[current_extruder] >45)||(bed_temperature_initial_layer[current_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[current_extruder] >50)||(bed_temperature_initial_layer[current_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n\nM104 S{nozzle_temperature_initial_layer[initial_extruder]} ; set extrude temp earlier, to reduce wait time\n\n;===== mech mode fast check============================\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q1 A7 B30 C80 H15 K0\nM974 Q1 S2 P0\n\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q0 A7 B30 C90 Q0 H15 K0\nM974 Q0 S2 P0\n\nM975 S1\nG1 F30000\nG1 X230 Y15\nG28 X ; re-home XY \n;===== fmech mode fast check============================\n\n\n;===== noozle load line ===============================\nM975 S1\nG90 \nM83\nT1000\nG1 X18.0 Y1.0 Z0.8 F18000;Move to start position\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG1 Z0.2\nG0 E2 F300\nG0 X240 E15 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \nG0 Y11 E0.700 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\nG0 X239.5\nG0 E0.2\nG0 Y1.5 E0.700\nG0 X18 E15 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \nM400\n\n;===== for Textured PEI Plate , lower the nozzle as the nozzle was touching topmost of the texture when homing ==\n;curr_bed_type={curr_bed_type}\n{if curr_bed_type==\"Textured PEI Plate\"}\nG29.1 Z{-0.04} ; for Textured PEI Plate\n{endif}\n;========turn off light and wait extrude temperature =============\nM1002 gcode_claim_action : 0\nM106 S0 ; turn off fan\nM106 P2 S0 ; turn off big fan \nM106 P3 S0 ; turn off chamber fan\n\nM975 S1 ; turn on mech mode supression", + "extruder_clearance_max_radius": "56", + "machine_start_gcode": ";===== machine: P1P ========================\n;===== date: 20230323 =====================\n;===== turn on the HB fan =================\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp to turn on the HB fan\n;===== reset machine status =================\nG91\nM17 Z0.4 ; lower the z-motor current\nG0 Z12 F300 ; lower the hotbed , to prevent the nozzle is below the hotbed\nG0 Z-6;\nG90\nM17 X1.2 Y1.2 Z0.75 ; reset motor current to default\nM960 S5 P1 ; turn on logo lamp\nG90\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\nM73.2 R1.0 ;Reset left time magnitude\nM1002 set_gcode_claim_speed_level : 5\nM221 X0 Y0 Z0 ; turn off soft endstop to prevent protential logic problem\nG29.1 Z{+0.0} ; clear z-trim value first\n\n;===== heatbed preheat ====================\nM1002 gcode_claim_action : 2\nM140 S[bed_temperature_initial_layer_single] ;set bed temp\nM190 S[bed_temperature_initial_layer_single] ;wait for bed temp\n\n\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_extruder]==\"PLA\"}\n {if (bed_temperature[initial_extruder] >45)||(bed_temperature_initial_layer[initial_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[initial_extruder] >50)||(bed_temperature_initial_layer[initial_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n;===== prepare print temperature and material ==========\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp\nG91\nG0 Z10 F1200\nG90\nG28 X\nM975 S1 ; turn on\nG1 X60 F12000\nG1 Y245\nG1 Y265 F3000\nM620 M\nM620 S[initial_extruder]A ; switch material if AMS exist\n M109 S[nozzle_temperature_initial_layer]\n G1 X120 F12000\n\n G1 X20 Y50 F12000\n G1 Y-3\n T[initial_extruder]\n G1 X54 F12000\n G1 Y265\n M400\nM621 S[initial_extruder]A\nM620.1 E F{filament_max_volumetric_speed[initial_extruder]/2.4053*60} T{nozzle_temperature_range_high[initial_extruder]}\n\n\nM412 S1 ; ===turn on filament runout detection===\n\nM109 S250 ;set nozzle to common flush temp\nM106 P1 S0\nG92 E0\nG1 E50 F200\nM400\nM104 S[nozzle_temperature_initial_layer]\nG92 E0\nG1 E50 F200\nM400\nM106 P1 S255\nG92 E0\nG1 E5 F300\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20} ; drop nozzle temp, make filament shink a bit\nG92 E0\nG1 E-0.5 F300\n\nG1 X70 F9000\nG1 X76 F15000\nG1 X65 F15000\nG1 X76 F15000\nG1 X65 F15000; shake to put down garbage\nG1 X80 F6000\nG1 X95 F15000\nG1 X80 F15000\nG1 X165 F15000; wipe and shake\nM400\nM106 P1 S0\n;===== prepare print temperature and material end =====\n\n\n;===== wipe nozzle ===============================\nM1002 gcode_claim_action : 14\nM975 S1\nM106 S255\nG1 X65 Y230 F18000\nG1 Y264 F6000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20}\nG1 X100 F18000 ; first wipe mouth\n\nG0 X135 Y253 F20000 ; move to exposed steel surface edge\nG28 Z P0 T300; home z with low precision,permit 300deg temperature\nG29.2 S0 ; turn off ABL\nG0 Z5 F20000\n\nG1 X60 Y265\nG92 E0\nG1 E-0.5 F300 ; retrack more\nG1 X100 F5000; second wipe mouth\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X90 F5000\nG0 X128 Y261 Z-1.5 F20000 ; move to exposed steel surface and stop the nozzle\nM104 S140 ; set temp down to heatbed acceptable\nM106 S255 ; turn on fan (G28 has turn off fan)\n\nM221 S; push soft endstop status\nM221 Z0 ;turn off Z axis endstop\nG0 Z0.5 F20000\nG0 X125 Y259.5 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 X128\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\n\nM109 S140 ; wait nozzle temp down to heatbed acceptable\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\n\nM221 R; pop softend status\nG1 Z10 F1200\nM400\nG1 Z10\nG1 F30000\nG1 X230 Y15\nG29.2 S1 ; turn on ABL\n;G28 ; home again after hard wipe mouth\nM106 S0 ; turn off fan , too noisy\n;===== wipe nozzle end ================================\n\n\n;===== bed leveling ==================================\nM1002 judge_flag g29_before_print_flag\nM622 J1\n\n M1002 gcode_claim_action : 1\n G29 A X{first_layer_print_min[0]} Y{first_layer_print_min[1]} I{first_layer_print_size[0]} J{first_layer_print_size[1]}\n M400\n M500 ; save cali data\n\nM623\n;===== bed leveling end ================================\n\n;===== home after wipe mouth============================\nM1002 judge_flag g29_before_print_flag\nM622 J0\n\n M1002 gcode_claim_action : 13\n G28\n\nM623\n;===== home after wipe mouth end =======================\n\nM975 S1 ; turn on vibration supression\n\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_extruder]==\"PLA\"}\n {if (bed_temperature[initial_extruder] >45)||(bed_temperature_initial_layer[initial_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[initial_extruder] >50)||(bed_temperature_initial_layer[initial_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n\nM104 S{nozzle_temperature_initial_layer[initial_extruder]} ; set extrude temp earlier, to reduce wait time\n\n;===== mech mode fast check============================\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q1 A7 B30 C80 H15 K0\nM974 Q1 S2 P0\n\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q0 A7 B30 C90 Q0 H15 K0\nM974 Q0 S2 P0\n\nM975 S1\nG1 F30000\nG1 X230 Y15\nG28 X ; re-home XY\n;===== fmech mode fast check============================\n\n\n;===== noozle load line ===============================\nM975 S1\nG90\nM83\nT1000\nG1 X18.0 Y1.0 Z0.8 F18000;Move to start position\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG1 Z0.2\nG0 E2 F300\nG0 X240 E15 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\nG0 Y11 E0.700 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\nG0 X239.5\nG0 E0.2\nG0 Y1.5 E0.700\nG0 X18 E15 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\nM400\n\n;===== for Textured PEI Plate , lower the nozzle as the nozzle was touching topmost of the texture when homing ==\n;curr_bed_type={curr_bed_type}\n{if curr_bed_type==\"Textured PEI Plate\"}\nG29.1 Z{-0.04} ; for Textured PEI Plate\n{endif}\n;========turn off light and wait extrude temperature =============\nM1002 gcode_claim_action : 0\nM106 S0 ; turn off fan\nM106 P2 S0 ; turn off big fan\nM106 P3 S0 ; turn off chamber fan\n\nM975 S1 ; turn on mech mode supression", "scan_first_layer": "0", "machine_load_filament_time": "29", "machine_unload_filament_time": "28", "nozzle_type": "stainless_steel", "nozzle_hrc": "20", "auxiliary_fan": "1", + "layer_change_gcode": "; layer num/total_layer_count: {layer_num+1}/[total_layer_count]\nM622.1 S1 ; for prev firware, default turned on\nM1002 judge_flag timelapse_record_flag\nM622 J1\n{if timelapse_type == 0} ; timelapse without wipe tower\nM971 S11 C10 O0\n{elsif timelapse_type == 1} ; timelapse with wipe tower\nG92 E0\nG1 E-[retraction_length] F1800\nG17\nG2 Z{layer_z + 0.4} I0.86 J0.86 P1 F20000 ; spiral lift a little\nG1 X65 Y245 F20000 ; move to safe pos\nG17\nG2 Z{layer_z} I0.86 J0.86 P1 F20000\nG1 Y265 F3000\nM400 P300\nM971 S11 C11 O0\nG92 E0\nG1 E[retraction_length] F300\nG1 X100 F5000\nG1 Y255 F20000\n{endif}\nM623\n; update layer progress\nM73 L{layer_num+1}\nM991 S0 P{layer_num} ;notify layer change", "upward_compatible_machine":[ "Bambu Lab X1 0.4 nozzle", "Bambu Lab X1 Carbon 0.4 nozzle" diff --git a/resources/profiles/BBL/machine/Bambu Lab P1P 0.6 nozzle.json b/resources/profiles/BBL/machine/Bambu Lab P1P 0.6 nozzle.json index 1e6bc5d45d..d5e2180d11 100644 --- a/resources/profiles/BBL/machine/Bambu Lab P1P 0.6 nozzle.json +++ b/resources/profiles/BBL/machine/Bambu Lab P1P 0.6 nozzle.json @@ -26,7 +26,7 @@ "retraction_minimum_travel": [ "3" ], - "machine_start_gcode": ";===== machine: P1P ========================\n;===== date: 202230129 =====================\n;===== reset machine status =================\nG91\nM17 Z0.4 ; lower the z-motor current\nG0 Z12 F300 ; lower the hotbed , to prevent the nozzle is below the hotbed\nG0 Z-6;\nG90\nM17 X1.2 Y1.2 Z0.75 ; reset motor current to default\nM960 S5 P1 ; turn on logo lamp\nG90\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\nM73.2 R1.0 ;Reset left time magnitude\nM1002 set_gcode_claim_speed_level : 5\nM221 X0 Y0 Z0 ; turn off soft endstop to prevent protential logic problem\nG29.1 Z{+0.0} ; clear z-trim value first\n\n;===== heatbed preheat ====================\nM1002 gcode_claim_action : 2\nM140 S[bed_temperature_initial_layer_single] ;set bed temp\nM190 S[bed_temperature_initial_layer_single] ;wait for bed temp\n\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_tool]==\"PLA\"}\n {if (bed_temperature[current_extruder] >45)||(bed_temperature_initial_layer[current_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[current_extruder] >50)||(bed_temperature_initial_layer[current_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n;===== prepare print temperature and material ==========\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp\nG91\nG0 Z10 F1200\nG90\nG28 X\nM975 S1 ; turn on \nG1 X60 F12000\nG1 Y245\nG1 Y265 F3000\nM620 M\nM620 S[initial_tool]A ; switch material if AMS exist\n M109 S[nozzle_temperature_initial_layer]\n G1 X120 F12000\n\n G1 X20 Y50 F12000\n G1 Y-3\n T[initial_tool]\n G1 X54 F12000\n G1 Y265\n M400\nM621 S[initial_tool]A\n\nM412 S1 ; ===turn on filament runout detection===\n\nM109 S250 ;set nozzle to common flush temp\nM106 P1 S0\nG92 E0\nG1 E50 F200\nM400\nM104 S[nozzle_temperature_initial_layer]\nG92 E0\nG1 E50 F200\nM400\nM106 P1 S255\nG92 E0\nG1 E5 F300\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20} ; drop nozzle temp, make filament shink a bit\nG92 E0\nG1 E-0.5 F300\n\nG1 X70 F9000\nG1 X76 F15000\nG1 X65 F15000\nG1 X76 F15000\nG1 X65 F15000; shake to put down garbage\nG1 X80 F6000\nG1 X95 F15000\nG1 X80 F15000\nG1 X165 F15000; wipe and shake\nM400\nM106 P1 S0\n;===== prepare print temperature and material end =====\n\n\n;===== wipe nozzle ===============================\nM1002 gcode_claim_action : 14\nM975 S1\nM106 S255\nG1 X65 Y230 F18000\nG1 Y264 F6000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20}\nG1 X100 F18000 ; first wipe mouth\n\nG0 X135 Y253 F20000 ; move to exposed steel surface edge\nG28 Z P0 T300; home z with low precision,permit 300deg temperature\nG29.2 S0 ; turn off ABL\nG0 Z5 F20000\n\nG1 X60 Y265\nG92 E0\nG1 E-0.5 F300 ; retrack more\nG1 X100 F5000; second wipe mouth\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X90 F5000\nG0 X128 Y261 Z-1.5 F20000 ; move to exposed steel surface and stop the nozzle\nM104 S140 ; set temp down to heatbed acceptable\nM106 S255 ; turn on fan (G28 has turn off fan)\n\nM221 S; push soft endstop status\nM221 Z0 ;turn off Z axis endstop\nG0 Z0.5 F20000\nG0 X125 Y259.5 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 X128\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\n\nM109 S140 ; wait nozzle temp down to heatbed acceptable\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\n\nM221 R; pop softend status\nG1 Z10 F1200\nM400\nG1 Z10\nG1 F30000\nG1 X230 Y15\nG29.2 S1 ; turn on ABL\n;G28 ; home again after hard wipe mouth\nM106 S0 ; turn off fan , too noisy\n;===== wipe nozzle end ================================\n\n\n;===== bed leveling ==================================\nM1002 judge_flag g29_before_print_flag\nM622 J1\n\n M1002 gcode_claim_action : 1\n G29 A X{first_layer_print_min[0]} Y{first_layer_print_min[1]} I{first_layer_print_size[0]} J{first_layer_print_size[1]}\n M400\n M500 ; save cali data\n\nM623\n;===== bed leveling end ================================\n\n;===== home after wipe mouth============================\nM1002 judge_flag g29_before_print_flag\nM622 J0\n\n M1002 gcode_claim_action : 13\n G28\n\nM623\n;===== home after wipe mouth end =======================\n\nM975 S1 ; turn on vibration supression\n\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_tool]==\"PLA\"}\n {if (bed_temperature[current_extruder] >45)||(bed_temperature_initial_layer[current_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[current_extruder] >50)||(bed_temperature_initial_layer[current_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n\nM104 S{nozzle_temperature_initial_layer[initial_extruder]} ; set extrude temp earlier, to reduce wait time\n\n;===== mech mode fast check============================\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q1 A7 B30 C80 H15 K0\nM974 Q1 S2 P0\n\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q0 A7 B30 C90 Q0 H15 K0\nM974 Q0 S2 P0\n\nM975 S1\nG1 F30000\nG1 X230 Y15\nG28 X ; re-home XY \n;===== fmech mode fast check============================\n\n\n;===== noozle load line ===============================\nM975 S1\nG90 \nM83\nT1000\nG1 X18.0 Y1.0 Z0.8 F18000;Move to start position\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG1 Z0.2\nG0 E2 F300\nG0 X240 E25 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \nG0 Y15 E1.166 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\nG0 X239.5\nG0 E0.2\nG0 Y1.5 E1.166\nG0 X18 E25 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \nM400\n\n;===== for Textured PEI Plate , lower the nozzle as the nozzle was touching topmost of the texture when homing ==\n;curr_bed_type={curr_bed_type}\n{if curr_bed_type==\"Textured PEI Plate\"}\nG29.1 Z{-0.04} ; for Textured PEI Plate\n{endif}\n;========turn off light and wait extrude temperature =============\nM1002 gcode_claim_action : 0\nM106 S0 ; turn off fan\nM106 P2 S0 ; turn off big fan \nM106 P3 S0 ; turn off chamber fan\n\nM975 S1 ; turn on mech mode supression", + "machine_start_gcode": ";===== machine: P1P ========================\n;===== date: 20230323 =====================\n;===== turn on the HB fan =================\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp to turn on the HB fan\n;===== reset machine status =================\nG91\nM17 Z0.4 ; lower the z-motor current\nG0 Z12 F300 ; lower the hotbed , to prevent the nozzle is below the hotbed\nG0 Z-6;\nG90\nM17 X1.2 Y1.2 Z0.75 ; reset motor current to default\nM960 S5 P1 ; turn on logo lamp\nG90\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\nM73.2 R1.0 ;Reset left time magnitude\nM1002 set_gcode_claim_speed_level : 5\nM221 X0 Y0 Z0 ; turn off soft endstop to prevent protential logic problem\nG29.1 Z{+0.0} ; clear z-trim value first\n\n;===== heatbed preheat ====================\nM1002 gcode_claim_action : 2\nM140 S[bed_temperature_initial_layer_single] ;set bed temp\nM190 S[bed_temperature_initial_layer_single] ;wait for bed temp\n\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_extruder]==\"PLA\"}\n {if (bed_temperature[initial_extruder] >45)||(bed_temperature_initial_layer[initial_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[initial_extruder] >50)||(bed_temperature_initial_layer[initial_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n;===== prepare print temperature and material ==========\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp\nG91\nG0 Z10 F1200\nG90\nG28 X\nM975 S1 ; turn on\nG1 X60 F12000\nG1 Y245\nG1 Y265 F3000\nM620 M\nM620 S[initial_extruder]A ; switch material if AMS exist\n M109 S[nozzle_temperature_initial_layer]\n G1 X120 F12000\n\n G1 X20 Y50 F12000\n G1 Y-3\n T[initial_extruder]\n G1 X54 F12000\n G1 Y265\n M400\nM621 S[initial_extruder]A\nM620.1 E F{filament_max_volumetric_speed[initial_extruder]/2.4053*60} T{nozzle_temperature_range_high[initial_extruder]}\n\n\nM412 S1 ; ===turn on filament runout detection===\n\nM109 S250 ;set nozzle to common flush temp\nM106 P1 S0\nG92 E0\nG1 E50 F200\nM400\nM104 S[nozzle_temperature_initial_layer]\nG92 E0\nG1 E50 F200\nM400\nM106 P1 S255\nG92 E0\nG1 E5 F300\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20} ; drop nozzle temp, make filament shink a bit\nG92 E0\nG1 E-0.5 F300\n\nG1 X70 F9000\nG1 X76 F15000\nG1 X65 F15000\nG1 X76 F15000\nG1 X65 F15000; shake to put down garbage\nG1 X80 F6000\nG1 X95 F15000\nG1 X80 F15000\nG1 X165 F15000; wipe and shake\nM400\nM106 P1 S0\n;===== prepare print temperature and material end =====\n\n\n;===== wipe nozzle ===============================\nM1002 gcode_claim_action : 14\nM975 S1\nM106 S255\nG1 X65 Y230 F18000\nG1 Y264 F6000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20}\nG1 X100 F18000 ; first wipe mouth\n\nG0 X135 Y253 F20000 ; move to exposed steel surface edge\nG28 Z P0 T300; home z with low precision,permit 300deg temperature\nG29.2 S0 ; turn off ABL\nG0 Z5 F20000\n\nG1 X60 Y265\nG92 E0\nG1 E-0.5 F300 ; retrack more\nG1 X100 F5000; second wipe mouth\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X90 F5000\nG0 X128 Y261 Z-1.5 F20000 ; move to exposed steel surface and stop the nozzle\nM104 S140 ; set temp down to heatbed acceptable\nM106 S255 ; turn on fan (G28 has turn off fan)\n\nM221 S; push soft endstop status\nM221 Z0 ;turn off Z axis endstop\nG0 Z0.5 F20000\nG0 X125 Y259.5 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 X128\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\n\nM109 S140 ; wait nozzle temp down to heatbed acceptable\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\n\nM221 R; pop softend status\nG1 Z10 F1200\nM400\nG1 Z10\nG1 F30000\nG1 X230 Y15\nG29.2 S1 ; turn on ABL\n;G28 ; home again after hard wipe mouth\nM106 S0 ; turn off fan , too noisy\n;===== wipe nozzle end ================================\n\n\n;===== bed leveling ==================================\nM1002 judge_flag g29_before_print_flag\nM622 J1\n\n M1002 gcode_claim_action : 1\n G29 A X{first_layer_print_min[0]} Y{first_layer_print_min[1]} I{first_layer_print_size[0]} J{first_layer_print_size[1]}\n M400\n M500 ; save cali data\n\nM623\n;===== bed leveling end ================================\n\n;===== home after wipe mouth============================\nM1002 judge_flag g29_before_print_flag\nM622 J0\n\n M1002 gcode_claim_action : 13\n G28\n\nM623\n;===== home after wipe mouth end =======================\n\nM975 S1 ; turn on vibration supression\n\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_extruder]==\"PLA\"}\n {if (bed_temperature[initial_extruder] >45)||(bed_temperature_initial_layer[initial_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[initial_extruder] >50)||(bed_temperature_initial_layer[initial_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n\nM104 S{nozzle_temperature_initial_layer[initial_extruder]} ; set extrude temp earlier, to reduce wait time\n\n;===== mech mode fast check============================\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q1 A7 B30 C80 H15 K0\nM974 Q1 S2 P0\n\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q0 A7 B30 C90 Q0 H15 K0\nM974 Q0 S2 P0\n\nM975 S1\nG1 F30000\nG1 X230 Y15\nG28 X ; re-home XY\n;===== fmech mode fast check============================\n\n\n;===== noozle load line ===============================\nM975 S1\nG90\nM83\nT1000\nG1 X18.0 Y1.0 Z0.8 F18000;Move to start position\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG1 Z0.2\nG0 E2 F300\nG0 X240 E25 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\nG0 Y15 E1.166 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\nG0 X239.5\nG0 E0.2\nG0 Y1.5 E1.166\nG0 X18 E25 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\nM400\n\n;===== for Textured PEI Plate , lower the nozzle as the nozzle was touching topmost of the texture when homing ==\n;curr_bed_type={curr_bed_type}\n{if curr_bed_type==\"Textured PEI Plate\"}\nG29.1 Z{-0.04} ; for Textured PEI Plate\n{endif}\n;========turn off light and wait extrude temperature =============\nM1002 gcode_claim_action : 0\nM106 S0 ; turn off fan\nM106 P2 S0 ; turn off big fan\nM106 P3 S0 ; turn off chamber fan\n\nM975 S1 ; turn on mech mode supression", "nozzle_type": "hardened_steel", "nozzle_hrc": "55", "upward_compatible_machine":[ diff --git a/resources/profiles/BBL/machine/Bambu Lab P1P 0.8 nozzle.json b/resources/profiles/BBL/machine/Bambu Lab P1P 0.8 nozzle.json index 551de284b2..cb10e3e27a 100644 --- a/resources/profiles/BBL/machine/Bambu Lab P1P 0.8 nozzle.json +++ b/resources/profiles/BBL/machine/Bambu Lab P1P 0.8 nozzle.json @@ -29,7 +29,7 @@ "retract_length_toolchange": [ "3" ], - "machine_start_gcode": ";===== machine: P1P ========================\n;===== date: 202230129 =====================\n;===== reset machine status =================\nG91\nM17 Z0.4 ; lower the z-motor current\nG0 Z12 F300 ; lower the hotbed , to prevent the nozzle is below the hotbed\nG0 Z-6;\nG90\nM17 X1.2 Y1.2 Z0.75 ; reset motor current to default\nM960 S5 P1 ; turn on logo lamp\nG90\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\nM73.2 R1.0 ;Reset left time magnitude\nM1002 set_gcode_claim_speed_level : 5\nM221 X0 Y0 Z0 ; turn off soft endstop to prevent protential logic problem\nG29.1 Z{+0.0} ; clear z-trim value first\n\n;===== heatbed preheat ====================\nM1002 gcode_claim_action : 2\nM140 S[bed_temperature_initial_layer_single] ;set bed temp\nM190 S[bed_temperature_initial_layer_single] ;wait for bed temp\n\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_tool]==\"PLA\"}\n {if (bed_temperature[current_extruder] >45)||(bed_temperature_initial_layer[current_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[current_extruder] >50)||(bed_temperature_initial_layer[current_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n;===== prepare print temperature and material ==========\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp\nG91\nG0 Z10 F1200\nG90\nG28 X\nM975 S1 ; turn on \nG1 X60 F12000\nG1 Y245\nG1 Y265 F3000\nM620 M\nM620 S[initial_tool]A ; switch material if AMS exist\n M109 S[nozzle_temperature_initial_layer]\n G1 X120 F12000\n\n G1 X20 Y50 F12000\n G1 Y-3\n T[initial_tool]\n G1 X54 F12000\n G1 Y265\n M400\nM621 S[initial_tool]A\n\nM412 S1 ; ===turn on filament runout detection===\n\nM109 S250 ;set nozzle to common flush temp\nM106 P1 S0\nG92 E0\nG1 E50 F200\nM400\nM104 S[nozzle_temperature_initial_layer]\nG92 E0\nG1 E50 F200\nM400\nM106 P1 S255\nG92 E0\nG1 E5 F300\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20} ; drop nozzle temp, make filament shink a bit\nG92 E0\nG1 E-0.5 F300\n\nG1 X70 F9000\nG1 X76 F15000\nG1 X65 F15000\nG1 X76 F15000\nG1 X65 F15000; shake to put down garbage\nG1 X80 F6000\nG1 X95 F15000\nG1 X80 F15000\nG1 X165 F15000; wipe and shake\nM400\nM106 P1 S0\n;===== prepare print temperature and material end =====\n\n\n;===== wipe nozzle ===============================\nM1002 gcode_claim_action : 14\nM975 S1\nM106 S255\nG1 X65 Y230 F18000\nG1 Y264 F6000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20}\nG1 X100 F18000 ; first wipe mouth\n\nG0 X135 Y253 F20000 ; move to exposed steel surface edge\nG28 Z P0 T300; home z with low precision,permit 300deg temperature\nG29.2 S0 ; turn off ABL\nG0 Z5 F20000\n\nG1 X60 Y265\nG92 E0\nG1 E-0.5 F300 ; retrack more\nG1 X100 F5000; second wipe mouth\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X90 F5000\nG0 X128 Y261 Z-1.5 F20000 ; move to exposed steel surface and stop the nozzle\nM104 S140 ; set temp down to heatbed acceptable\nM106 S255 ; turn on fan (G28 has turn off fan)\n\nM221 S; push soft endstop status\nM221 Z0 ;turn off Z axis endstop\nG0 Z0.5 F20000\nG0 X125 Y259.5 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 X128\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\n\nM109 S140 ; wait nozzle temp down to heatbed acceptable\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\n\nM221 R; pop softend status\nG1 Z10 F1200\nM400\nG1 Z10\nG1 F30000\nG1 X230 Y15\nG29.2 S1 ; turn on ABL\n;G28 ; home again after hard wipe mouth\nM106 S0 ; turn off fan , too noisy\n;===== wipe nozzle end ================================\n\n\n;===== bed leveling ==================================\nM1002 judge_flag g29_before_print_flag\nM622 J1\n\n M1002 gcode_claim_action : 1\n G29 A X{first_layer_print_min[0]} Y{first_layer_print_min[1]} I{first_layer_print_size[0]} J{first_layer_print_size[1]}\n M400\n M500 ; save cali data\n\nM623\n;===== bed leveling end ================================\n\n;===== home after wipe mouth============================\nM1002 judge_flag g29_before_print_flag\nM622 J0\n\n M1002 gcode_claim_action : 13\n G28\n\nM623\n;===== home after wipe mouth end =======================\n\nM975 S1 ; turn on vibration supression\n\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_tool]==\"PLA\"}\n {if (bed_temperature[current_extruder] >45)||(bed_temperature_initial_layer[current_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[current_extruder] >50)||(bed_temperature_initial_layer[current_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n\nM104 S{nozzle_temperature_initial_layer[initial_extruder]} ; set extrude temp earlier, to reduce wait time\n\n;===== mech mode fast check============================\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q1 A7 B30 C80 H15 K0\nM974 Q1 S2 P0\n\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q0 A7 B30 C90 Q0 H15 K0\nM974 Q0 S2 P0\n\nM975 S1\nG1 F30000\nG1 X230 Y15\nG28 X ; re-home XY \n;===== fmech mode fast check============================\n\n\n;===== noozle load line ===============================\nM975 S1\nG90 \nM83\nT1000\nG1 X18.0 Y0.5 Z0.8 F18000;Move to start position\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG1 Z0.2\nG0 E2 F300\nG0 X129 E15 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \nG0 X240 E15\nG0 Y15 E1.500 F{outer_wall_volumetric_speed/(0.3*1.0)/ 4 * 60}\nG0 X239.5\nG0 E0.3\nG0 Y1.5 E1.500\nG0 X129 E15 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \nG0 X18 E15\nM400\n\n;===== for Textured PEI Plate , lower the nozzle as the nozzle was touching topmost of the texture when homing ==\n;curr_bed_type={curr_bed_type}\n{if curr_bed_type==\"Textured PEI Plate\"}\nG29.1 Z{-0.04} ; for Textured PEI Plate\n{endif}\n;========turn off light and wait extrude temperature =============\nM1002 gcode_claim_action : 0\nM106 S0 ; turn off fan\nM106 P2 S0 ; turn off big fan \nM106 P3 S0 ; turn off chamber fan\n\nM975 S1 ; turn on mech mode supression", + "machine_start_gcode": ";===== machine: P1P ========================\n;===== date: 20230323 =====================\n;===== turn on the HB fan =================\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp to turn on the HB fan\n;===== reset machine status =================\nG91\nM17 Z0.4 ; lower the z-motor current\nG0 Z12 F300 ; lower the hotbed , to prevent the nozzle is below the hotbed\nG0 Z-6;\nG90\nM17 X1.2 Y1.2 Z0.75 ; reset motor current to default\nM960 S5 P1 ; turn on logo lamp\nG90\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\nM73.2 R1.0 ;Reset left time magnitude\nM1002 set_gcode_claim_speed_level : 5\nM221 X0 Y0 Z0 ; turn off soft endstop to prevent protential logic problem\nG29.1 Z{+0.0} ; clear z-trim value first\n\n;===== heatbed preheat ====================\nM1002 gcode_claim_action : 2\nM140 S[bed_temperature_initial_layer_single] ;set bed temp\nM190 S[bed_temperature_initial_layer_single] ;wait for bed temp\n\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_extruder]==\"PLA\"}\n {if (bed_temperature[initial_extruder] >45)||(bed_temperature_initial_layer[initial_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[initial_extruder] >50)||(bed_temperature_initial_layer[initial_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n;===== prepare print temperature and material ==========\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp\nG91\nG0 Z10 F1200\nG90\nG28 X\nM975 S1 ; turn on\nG1 X60 F12000\nG1 Y245\nG1 Y265 F3000\nM620 M\nM620 S[initial_extruder]A ; switch material if AMS exist\n M109 S[nozzle_temperature_initial_layer]\n G1 X120 F12000\n\n G1 X20 Y50 F12000\n G1 Y-3\n T[initial_extruder]\n G1 X54 F12000\n G1 Y265\n M400\nM621 S[initial_extruder]A\nM620.1 E F{filament_max_volumetric_speed[initial_extruder]/2.4053*60} T{nozzle_temperature_range_high[initial_extruder]}\n\n\nM412 S1 ; ===turn on filament runout detection===\n\nM109 S250 ;set nozzle to common flush temp\nM106 P1 S0\nG92 E0\nG1 E50 F200\nM400\nM104 S[nozzle_temperature_initial_layer]\nG92 E0\nG1 E50 F200\nM400\nM106 P1 S255\nG92 E0\nG1 E5 F300\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20} ; drop nozzle temp, make filament shink a bit\nG92 E0\nG1 E-0.5 F300\n\nG1 X70 F9000\nG1 X76 F15000\nG1 X65 F15000\nG1 X76 F15000\nG1 X65 F15000; shake to put down garbage\nG1 X80 F6000\nG1 X95 F15000\nG1 X80 F15000\nG1 X165 F15000; wipe and shake\nM400\nM106 P1 S0\n;===== prepare print temperature and material end =====\n\n\n;===== wipe nozzle ===============================\nM1002 gcode_claim_action : 14\nM975 S1\nM106 S255\nG1 X65 Y230 F18000\nG1 Y264 F6000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20}\nG1 X100 F18000 ; first wipe mouth\n\nG0 X135 Y253 F20000 ; move to exposed steel surface edge\nG28 Z P0 T300; home z with low precision,permit 300deg temperature\nG29.2 S0 ; turn off ABL\nG0 Z5 F20000\n\nG1 X60 Y265\nG92 E0\nG1 E-0.5 F300 ; retrack more\nG1 X100 F5000; second wipe mouth\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X90 F5000\nG0 X128 Y261 Z-1.5 F20000 ; move to exposed steel surface and stop the nozzle\nM104 S140 ; set temp down to heatbed acceptable\nM106 S255 ; turn on fan (G28 has turn off fan)\n\nM221 S; push soft endstop status\nM221 Z0 ;turn off Z axis endstop\nG0 Z0.5 F20000\nG0 X125 Y259.5 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 X128\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\n\nM109 S140 ; wait nozzle temp down to heatbed acceptable\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\n\nM221 R; pop softend status\nG1 Z10 F1200\nM400\nG1 Z10\nG1 F30000\nG1 X230 Y15\nG29.2 S1 ; turn on ABL\n;G28 ; home again after hard wipe mouth\nM106 S0 ; turn off fan , too noisy\n;===== wipe nozzle end ================================\n\n\n;===== bed leveling ==================================\nM1002 judge_flag g29_before_print_flag\nM622 J1\n\n M1002 gcode_claim_action : 1\n G29 A X{first_layer_print_min[0]} Y{first_layer_print_min[1]} I{first_layer_print_size[0]} J{first_layer_print_size[1]}\n M400\n M500 ; save cali data\n\nM623\n;===== bed leveling end ================================\n\n;===== home after wipe mouth============================\nM1002 judge_flag g29_before_print_flag\nM622 J0\n\n M1002 gcode_claim_action : 13\n G28\n\nM623\n;===== home after wipe mouth end =======================\n\nM975 S1 ; turn on vibration supression\n\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_extruder]==\"PLA\"}\n {if (bed_temperature[initial_extruder] >45)||(bed_temperature_initial_layer[initial_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[initial_extruder] >50)||(bed_temperature_initial_layer[initial_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n\nM104 S{nozzle_temperature_initial_layer[initial_extruder]} ; set extrude temp earlier, to reduce wait time\n\n;===== mech mode fast check============================\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q1 A7 B30 C80 H15 K0\nM974 Q1 S2 P0\n\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q0 A7 B30 C90 Q0 H15 K0\nM974 Q0 S2 P0\n\nM975 S1\nG1 F30000\nG1 X230 Y15\nG28 X ; re-home XY\n;===== fmech mode fast check============================\n\n\n;===== noozle load line ===============================\nM975 S1\nG90\nM83\nT1000\nG1 X18.0 Y0.5 Z0.8 F18000;Move to start position\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG1 Z0.2\nG0 E2 F300\nG0 X129 E15 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\nG0 X240 E15\nG0 Y15 E1.500 F{outer_wall_volumetric_speed/(0.3*1.0)/ 4 * 60}\nG0 X239.5\nG0 E0.3\nG0 Y1.5 E1.500\nG0 X129 E15 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\nG0 X18 E15\nM400\n\n;===== for Textured PEI Plate , lower the nozzle as the nozzle was touching topmost of the texture when homing ==\n;curr_bed_type={curr_bed_type}\n{if curr_bed_type==\"Textured PEI Plate\"}\nG29.1 Z{-0.04} ; for Textured PEI Plate\n{endif}\n;========turn off light and wait extrude temperature =============\nM1002 gcode_claim_action : 0\nM106 S0 ; turn off fan\nM106 P2 S0 ; turn off big fan\nM106 P3 S0 ; turn off chamber fan\n\nM975 S1 ; turn on mech mode supression", "nozzle_type": "hardened_steel", "nozzle_hrc": "55", "upward_compatible_machine":[ diff --git a/resources/profiles/BBL/machine/Bambu Lab X1 0.2 nozzle.json b/resources/profiles/BBL/machine/Bambu Lab X1 0.2 nozzle.json index 0d83b274d4..d727b43d07 100644 --- a/resources/profiles/BBL/machine/Bambu Lab X1 0.2 nozzle.json +++ b/resources/profiles/BBL/machine/Bambu Lab X1 0.2 nozzle.json @@ -26,7 +26,7 @@ "retraction_minimum_travel": [ "5" ], - "machine_start_gcode": ";===== machine: X1 =========================\n;===== date: 20230130 =====================\n;===== reset machine status =================\nG91\nM17 Z0.4 ; lower the z-motor current\nG0 Z12 F300 ; lower the hotbed , to prevent the nozzle is below the hotbed\nG0 Z-6;\nG90\nM17 X1.2 Y1.2 Z0.75 ; reset motor current to default\nM960 S5 P1 ; turn on logo lamp\nG90\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\nM73.2 R1.0 ;Reset left time magnitude\nM1002 set_gcode_claim_speed_level : 5\nM221 X0 Y0 Z0 ; turn off soft endstop to prevent protential logic problem\nG29.1 Z{+0.0} ; clear z-trim value first\n\n;===== heatbed preheat ====================\nM1002 gcode_claim_action : 2\nM140 S[bed_temperature_initial_layer_single] ;set bed temp\nM190 S[bed_temperature_initial_layer_single] ;wait for bed temp\n\n{if scan_first_layer}\n;=========register first layer scan=====\nM977 S1 P60\n{endif}\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_tool]==\"PLA\"}\n {if (bed_temperature[current_extruder] >45)||(bed_temperature_initial_layer[current_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[current_extruder] >50)||(bed_temperature_initial_layer[current_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n;===== prepare print temperature and material ==========\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp\nG91\nG0 Z10 F1200\nG90\nG28 X\nM975 S1 ; turn on \nG1 X60 F12000\nG1 Y245\nG1 Y265 F3000\nM620 M\nM620 S[initial_tool]A ; switch material if AMS exist\n M109 S[nozzle_temperature_initial_layer]\n G1 X120 F12000\n\n G1 X20 Y50 F12000\n G1 Y-3\n T[initial_tool]\n G1 X54 F12000\n G1 Y265\n M400\nM621 S[initial_tool]A\n\nM412 S1 ; ===turn on filament runout detection===\n\nM109 S250 ;set nozzle to common flush temp\nM106 P1 S0\nG92 E0\nG1 E50 F200\nM400\nM104 S[nozzle_temperature_initial_layer]\nG92 E0\nG1 E50 F200\nM400\nM106 P1 S255\nG92 E0\nG1 E5 F300\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20} ; drop nozzle temp, make filament shink a bit\nG92 E0\nG1 E-0.5 F300\n\nG1 X70 F9000\nG1 X76 F15000\nG1 X65 F15000\nG1 X76 F15000\nG1 X65 F15000; shake to put down garbage\nG1 X80 F6000\nG1 X95 F15000\nG1 X80 F15000\nG1 X165 F15000; wipe and shake\nM400\nM106 P1 S0\n;===== prepare print temperature and material end =====\n\n\n;===== wipe nozzle ===============================\nM1002 gcode_claim_action : 14\nM975 S1\nM106 S255\nG1 X65 Y230 F18000\nG1 Y264 F6000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20}\nG1 X100 F18000 ; first wipe mouth\n\nG0 X135 Y253 F20000 ; move to exposed steel surface edge\nG28 Z P0 T300; home z with low precision,permit 300deg temperature\nG29.2 S0 ; turn off ABL\nG0 Z5 F20000\n\nG1 X60 Y265\nG92 E0\nG1 E-0.5 F300 ; retrack more\nG1 X100 F5000; second wipe mouth\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X90 F5000\nG0 X128 Y261 Z-1.5 F20000 ; move to exposed steel surface and stop the nozzle\nM104 S140 ; set temp down to heatbed acceptable\nM106 S255 ; turn on fan (G28 has turn off fan)\n\nM221 S; push soft endstop status\nM221 Z0 ;turn off Z axis endstop\nG0 Z0.5 F20000\nG0 X125 Y259.5 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 X128\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\n\nM109 S140 ; wait nozzle temp down to heatbed acceptable\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\n\nM221 R; pop softend status\nG1 Z10 F1200\nM400\nG1 Z10\nG1 F30000\nG1 X128 Y128\nG29.2 S1 ; turn on ABL\n;G28 ; home again after hard wipe mouth\nM106 S0 ; turn off fan , too noisy\n;===== wipe nozzle end ================================\n\n;===== check scanner clarity ===========================\nG1 X128 Y128 F24000\nG28 Z P0\nM972 S5 P0\nG1 X230 Y15 F24000\n;===== check scanner clarity end =======================\n\n;===== bed leveling ==================================\nM1002 judge_flag g29_before_print_flag\nM622 J1\n\n M1002 gcode_claim_action : 1\n G29 A X{first_layer_print_min[0]} Y{first_layer_print_min[1]} I{first_layer_print_size[0]} J{first_layer_print_size[1]}\n M400\n M500 ; save cali data\n\nM623\n;===== bed leveling end ================================\n\n;===== home after wipe mouth============================\nM1002 judge_flag g29_before_print_flag\nM622 J0\n\n M1002 gcode_claim_action : 13\n G28\n\nM623\n;===== home after wipe mouth end =======================\n\nM975 S1 ; turn on vibration supression\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_tool]==\"PLA\"}\n {if (bed_temperature[current_extruder] >45)||(bed_temperature_initial_layer[current_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[current_extruder] >50)||(bed_temperature_initial_layer[current_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\nM104 S{nozzle_temperature_initial_layer[initial_extruder]} ; set extrude temp earlier, to reduce wait time\n\n;===== mech mode fast check============================\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q1 A7 B30 C80 H15 K0\nM974 Q1 S2 P0\n\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q0 A7 B30 C90 Q0 H15 K0\nM974 Q0 S2 P0\n\nM975 S1\nG1 F30000\nG1 X230 Y15\nG28 X ; re-home XY \n;===== mech mode fast check============================\n\n{if scan_first_layer}\n;start heatbed scan====================================\nM976 S2 P1 \nG90\nG1 X128 Y128 F20000\nM976 S3 P2 ;register void printing detection\n{endif}\n\n;===== noozle load line ===============================\nM975 S1\nG90 \nM83\nT1000\nG1 X18.0 Y1.0 Z0.8 F18000;Move to start position\nM109 S{nozzle_temperature[initial_extruder]}\nG1 Z0.2\nG0 E2 F300\nG0 X240 E15 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \nG0 Y11 E0.700 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\nG0 X239.5\nG0 E0.2\nG0 Y1.5 E0.700\nG0 X18 E15 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \nM400\n\n;===== for Textured PEI Plate , lower the nozzle as the nozzle was touching topmost of the texture when homing ==\n;curr_bed_type={curr_bed_type}\n{if curr_bed_type==\"Textured PEI Plate\"}\nG29.1 Z{-0.04} ; for Textured PEI Plate\n{endif}\n\n;===== draw extrinsic para cali paint =================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M1002 gcode_claim_action : 8\n\n T1000 \n G0 F3000 X28.000 Y19.500 Z0.200\n G1 F1200.0 X28.000 Y45.000 Z0.200 E0.933 \n G1 F1200.0 X28.500 Y45.000 Z0.200 E0.018 \n G1 F1200.0 X28.500 Y19.500 Z0.200 E0.933 \n G1 F1200.0 X31.000 Y19.500 Z0.200 E0.091 \n G1 F1200.0 X31.000 Y49.000 Z0.200 E1.080 \n G1 F1200.0 X37.500 Y49.000 Z0.200 E0.238 \n G1 F1200.0 X37.500 Y60.000 Z0.200 E0.403 \n G1 F1200.0 X42.500 Y60.000 Z0.200 E0.183 \n G1 F1200.0 X42.500 Y49.000 Z0.200 E0.403 \n G1 F1200.0 X48.000 Y49.000 Z0.200 E0.201 \n G1 F1200.0 X48.000 Y20.000 Z0.200 E1.061 \n G1 F1200.0 X30.000 Y20.000 Z0.200 E0.659 \n G1 F1200.0 X30.000 Y41.000 Z0.200 E0.769 \n G1 F1200.0 X50.000 Y41.000 Z0.200 E0.732 \n G1 F1200.0 X50.000 Y34.000 Z0.200 E0.256 \n G1 F1200.0 X30.000 Y34.000 Z0.200 E0.732 \n G1 F1500.000 E-0.800 \n\n ;=========== extruder cali extrusion ================== \n T1000 \n M83\n\t{if default_acceleration > 0}\n {if outer_wall_acceleration > 0}\n M204 S[outer_wall_acceleration]\n {else}\n M204 S[default_acceleration]\n {endif}\n {endif}\n G0 X35.000 Y18.000 Z0.300 F30000 E0\n G1 F1500.000 E0.800 \n M106 S0 ; turn off fan\n G0 X185.000 E9.35441 F4800 \n G0 X187 Z0\n G1 F1500.000 E-0.800 \n G0 Z1\n G0 X180 Z0.3 F18000\n \n M900 L1000.0 M1.0\n M900 K0.160 \n G0 X45.000 F30000 \n G0 Y20.000 F30000 \n G1 F1500.000 E0.800 \n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 F1500.000 E-0.800 \n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos \n G1 Z0.3\n M400\n\n G0 X45.000 F30000 \n M900 K0.080 \n G0 X45.000 F30000 \n G0 Y22.000 F30000 \n G1 F1500.000 E0.800 \n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 F1500.000 E-0.800 \n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos \n G1 Z0.3\n M400\n\n G0 X45.000 F30000 \n M900 K0.000 \n G0 X45.000 F30000 \n G0 Y24.000 F30000 \n G1 F1500.000 E0.800 \n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 F1500.000 E-0.800\n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos \n G1 Z0.3\n\n G0 X45.000 F30000 ; move to start point\n\nM623 ; end of \"draw extrinsic para cali paint\"\n\nM104 S140\n\n\n;=========== laser and rgb calibration =========== \nM400\nM18 E\nM500 R\n\nM973 S3 P14\n\nG1 X120 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\nT1100\nG1 X143.0 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\n\nM400 P100\n\nM960 S1 P1\nM400 P100\nM973 S6 P0; use auto exposure for horizontal laser by xcam\nM960 S0 P0\n\nG1 X240.0 Y6.0 Z0.3 F18000.0;Move to vertical extrude line pos\nM960 S2 P1\nM400 P100\nM973 S6 P1; use auto exposure for vertical laser by xcam\nM960 S0 P0\n\n;=========== handeye calibration ======================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M973 S3 P1 ; camera start stream\n M400 P500\n M973 S1 \n G0 F6000 X40.000 Y54.500 Z0.000 \n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500 \n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P1 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P200 \n M971 S5 P3 \n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P2 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P500 \n M971 S5 P4 \n M963 S1 \n M400 P1500 \n M964 \n T1100 \n G0 F6000 X40.000 Y54.500 Z0.000 \n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500 \n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P1 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P200 \n M971 S5 P3 \n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P2 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P500 \n M971 S5 P4 \n M963 S1 \n M400 P1500 \n M964 \n T1100 \n G1 Z3 F3000 \n\n M400\n M500 ; save cali data\n\n M104 S{nozzle_temperature[initial_extruder]} ; rise nozzle temp now ,to reduce temp waiting time.\n\n T1100 \n M400 P400 \n M960 S0 P0\n G0 F30000.000 Y22.000 X65.000 Z0.000\n M400 P400 \n M960 S1 P1 \n M400 P50 \n\n M969 S1 N3 A2000 \n G0 F360.000 X181.000 Z0.000\n M980.3 A70.000 B{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60/4} C5.000 D{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60} E5.000 F175.000 H1.000 I0.000 J0.080 K0.160\n M400 P100 \n G0 F20000\n G0 Z1 ; rise nozzle up\n T1000 ; change to nozzle space\n G0 X45.000 Y16.000 F30000 ; move to test line pos\n M969 S0 ; turn off scanning\n M960 S0 P0\n\n\n G1 Z2 F20000 \n T1000 \n G0 X45.000 Y16.000 F30000 E0\n M109 S{nozzle_temperature[initial_extruder]}\n G0 Z0.3\n G1 F1500.000 E3.600 \n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n\n ; see if extrude cali success, if not ,use default value\n M1002 judge_last_extrude_cali_success\n M622 J0\n M400\n M900 K0.08 M{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*0.08}\n M623 \n\n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X185.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X190.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X195.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X200.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X205.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X210.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X215.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X220.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X225.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n M973 S4 \n\nM623\n\n;========turn off light and wait extrude temperature =============\nM1002 gcode_claim_action : 0\nM973 S4 ; turn off scanner\nM400 ; wait all motion done before implement the emprical L parameters\n;M900 L500.0 ; Empirical parameters\nM109 S[nozzle_temperature_initial_layer]\nM960 S1 P0 ; turn off laser\nM960 S2 P0 ; turn off laser\nM106 S0 ; turn off fan\nM106 P2 S0 ; turn off big fan \nM106 P3 S0 ; turn off chamber fan\n\nM975 S1 ; turn on mech mode supression\nG90 \nM83\nT1000\nG1 E{-retraction_length[initial_extruder]} F1800\nG1 X128.0 Y253.0 Z0.2 F24000.0;Move to start position\nG1 E{retraction_length[initial_extruder]} F1800\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG0 X253 E6.4 F{outer_wall_volumetric_speed/(0.3*0.6) * 60} \nG0 Y128 E6.4\nG0 X252.5\nG0 Y252.5 E6.4\nG0 X128 E6.4", + "machine_start_gcode": ";===== machine: X1 =========================\n;===== date: 20230322 =====================\n;===== turn on the HB fan =================\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp to turn on the HB fan\n;===== reset machine status =================\nG91\nM17 Z0.4 ; lower the z-motor current\nG0 Z12 F300 ; lower the hotbed , to prevent the nozzle is below the hotbed\nG0 Z-6;\nG90\nM17 X1.2 Y1.2 Z0.75 ; reset motor current to default\nM960 S5 P1 ; turn on logo lamp\nG90\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\nM73.2 R1.0 ;Reset left time magnitude\nM1002 set_gcode_claim_speed_level : 5\nM221 X0 Y0 Z0 ; turn off soft endstop to prevent protential logic problem\nG29.1 Z{+0.0} ; clear z-trim value first\n\n;===== heatbed preheat ====================\nM1002 gcode_claim_action : 2\nM140 S[bed_temperature_initial_layer_single] ;set bed temp\nM190 S[bed_temperature_initial_layer_single] ;wait for bed temp\n\n{if scan_first_layer}\n;=========register first layer scan=====\nM977 S1 P60\n{endif}\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_no_support_extruder]==\"PLA\"}\n {if (bed_temperature[initial_no_support_extruder] >45)||(bed_temperature_initial_layer[initial_no_support_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[initial_no_support_extruder] >50)||(bed_temperature_initial_layer[initial_no_support_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n;===== prepare print temperature and material ==========\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp\nG91\nG0 Z10 F1200\nG90\nG28 X\nM975 S1 ; turn on\nG1 X60 F12000\nG1 Y245\nG1 Y265 F3000\nM620 M\nM620 S[initial_no_support_extruder]A ; switch material if AMS exist\n M109 S[nozzle_temperature_initial_layer]\n G1 X120 F12000\n\n G1 X20 Y50 F12000\n G1 Y-3\n T[initial_no_support_extruder]\n G1 X54 F12000\n G1 Y265\n M400\nM621 S[initial_no_support_extruder]A\nM620.1 E F{filament_max_volumetric_speed[initial_no_support_extruder]/2.4053*60} T{nozzle_temperature_range_high[initial_no_support_extruder]}\n\n\nM412 S1 ; ===turn on filament runout detection===\n\nM109 S250 ;set nozzle to common flush temp\nM106 P1 S0\nG92 E0\nG1 E50 F200\nM400\nM104 S[nozzle_temperature_initial_layer]\nG92 E0\nG1 E50 F200\nM400\nM106 P1 S255\nG92 E0\nG1 E5 F300\nM109 S{nozzle_temperature_initial_layer[initial_no_support_extruder]-20} ; drop nozzle temp, make filament shink a bit\nG92 E0\nG1 E-0.5 F300\n\nG1 X70 F9000\nG1 X76 F15000\nG1 X65 F15000\nG1 X76 F15000\nG1 X65 F15000; shake to put down garbage\nG1 X80 F6000\nG1 X95 F15000\nG1 X80 F15000\nG1 X165 F15000; wipe and shake\nM400\nM106 P1 S0\n;===== prepare print temperature and material end =====\n\n\n;===== wipe nozzle ===============================\nM1002 gcode_claim_action : 14\nM975 S1\nM106 S255\nG1 X65 Y230 F18000\nG1 Y264 F6000\nM109 S{nozzle_temperature_initial_layer[initial_no_support_extruder]-20}\nG1 X100 F18000 ; first wipe mouth\n\nG0 X135 Y253 F20000 ; move to exposed steel surface edge\nG28 Z P0 T300; home z with low precision,permit 300deg temperature\nG29.2 S0 ; turn off ABL\nG0 Z5 F20000\n\nG1 X60 Y265\nG92 E0\nG1 E-0.5 F300 ; retrack more\nG1 X100 F5000; second wipe mouth\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X90 F5000\nG0 X128 Y261 Z-1.5 F20000 ; move to exposed steel surface and stop the nozzle\nM104 S140 ; set temp down to heatbed acceptable\nM106 S255 ; turn on fan (G28 has turn off fan)\n\nM221 S; push soft endstop status\nM221 Z0 ;turn off Z axis endstop\nG0 Z0.5 F20000\nG0 X125 Y259.5 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 X128\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\n\nM109 S140 ; wait nozzle temp down to heatbed acceptable\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\n\nM221 R; pop softend status\nG1 Z10 F1200\nM400\nG1 Z10\nG1 F30000\nG1 X128 Y128\nG29.2 S1 ; turn on ABL\n;G28 ; home again after hard wipe mouth\nM106 S0 ; turn off fan , too noisy\n;===== wipe nozzle end ================================\n\n;===== check scanner clarity ===========================\nG1 X128 Y128 F24000\nG28 Z P0\nM972 S5 P0\nG1 X230 Y15 F24000\n;===== check scanner clarity end =======================\n\n;===== bed leveling ==================================\nM1002 judge_flag g29_before_print_flag\nM622 J1\n\n M1002 gcode_claim_action : 1\n G29 A X{first_layer_print_min[0]} Y{first_layer_print_min[1]} I{first_layer_print_size[0]} J{first_layer_print_size[1]}\n M400\n M500 ; save cali data\n\nM623\n;===== bed leveling end ================================\n\n;===== home after wipe mouth============================\nM1002 judge_flag g29_before_print_flag\nM622 J0\n\n M1002 gcode_claim_action : 13\n G28\n\nM623\n;===== home after wipe mouth end =======================\n\nM975 S1 ; turn on vibration supression\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_no_support_extruder]==\"PLA\"}\n {if (bed_temperature[initial_no_support_extruder] >45)||(bed_temperature_initial_layer[initial_no_support_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[initial_no_support_extruder] >50)||(bed_temperature_initial_layer[initial_no_support_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\nM104 S{nozzle_temperature_initial_layer[initial_no_support_extruder]} ; set extrude temp earlier, to reduce wait time\n\n;===== mech mode fast check============================\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q1 A7 B30 C80 H15 K0\nM974 Q1 S2 P0\n\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q0 A7 B30 C90 Q0 H15 K0\nM974 Q0 S2 P0\n\nM975 S1\nG1 F30000\nG1 X230 Y15\nG28 X ; re-home XY\n;===== mech mode fast check============================\n\n{if scan_first_layer}\n;start heatbed scan====================================\nM976 S2 P1\nG90\nG1 X128 Y128 F20000\nM976 S3 P2 ;register void printing detection\n{endif}\n\n;===== noozle load line ===============================\nM975 S1\nG90\nM83\nT1000\nG1 X18.0 Y1.0 Z0.8 F18000;Move to start position\nM109 S{nozzle_temperature[initial_no_support_extruder]}\nG1 Z0.2\nG0 E2 F300\nG0 X240 E15 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\nG0 Y11 E0.700 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\nG0 X239.5\nG0 E0.2\nG0 Y1.5 E0.700\nG0 X18 E15 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\nM400\n\n;===== for Textured PEI Plate , lower the nozzle as the nozzle was touching topmost of the texture when homing ==\n;curr_bed_type={curr_bed_type}\n{if curr_bed_type==\"Textured PEI Plate\"}\nG29.1 Z{-0.04} ; for Textured PEI Plate\n{endif}\n\n;===== draw extrinsic para cali paint =================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M1002 gcode_claim_action : 8\n\n T1000\n G0 F3000 X28.000 Y19.500 Z0.200\n G1 F1200.0 X28.000 Y45.000 Z0.200 E0.933\n G1 F1200.0 X28.500 Y45.000 Z0.200 E0.018\n G1 F1200.0 X28.500 Y19.500 Z0.200 E0.933\n G1 F1200.0 X31.000 Y19.500 Z0.200 E0.091\n G1 F1200.0 X31.000 Y49.000 Z0.200 E1.080\n G1 F1200.0 X37.500 Y49.000 Z0.200 E0.238\n G1 F1200.0 X37.500 Y60.000 Z0.200 E0.403\n G1 F1200.0 X42.500 Y60.000 Z0.200 E0.183\n G1 F1200.0 X42.500 Y49.000 Z0.200 E0.403\n G1 F1200.0 X48.000 Y49.000 Z0.200 E0.201\n G1 F1200.0 X48.000 Y20.000 Z0.200 E1.061\n G1 F1200.0 X30.000 Y20.000 Z0.200 E0.659\n G1 F1200.0 X30.000 Y41.000 Z0.200 E0.769\n G1 F1200.0 X50.000 Y41.000 Z0.200 E0.732\n G1 F1200.0 X50.000 Y34.000 Z0.200 E0.256\n G1 F1200.0 X30.000 Y34.000 Z0.200 E0.732\n G1 F1500.000 E-0.800\n\n ;=========== extruder cali extrusion ==================\n T1000\n M83\n\t{if default_acceleration > 0}\n {if outer_wall_acceleration > 0}\n M204 S[outer_wall_acceleration]\n {else}\n M204 S[default_acceleration]\n {endif}\n {endif}\n G0 X35.000 Y18.000 Z0.300 F30000 E0\n G1 F1500.000 E0.800\n M106 S0 ; turn off fan\n G0 X185.000 E9.35441 F4800\n G0 X187 Z0\n G1 F1500.000 E-0.800\n G0 Z1\n G0 X180 Z0.3 F18000\n\n M900 L1000.0 M1.0\n M900 K0.160\n G0 X45.000 F30000\n G0 Y20.000 F30000\n G1 F1500.000 E0.800\n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 F1500.000 E-0.800\n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos\n G1 Z0.3\n M400\n\n G0 X45.000 F30000\n M900 K0.080\n G0 X45.000 F30000\n G0 Y22.000 F30000\n G1 F1500.000 E0.800\n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 F1500.000 E-0.800\n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos\n G1 Z0.3\n M400\n\n G0 X45.000 F30000\n M900 K0.000\n G0 X45.000 F30000\n G0 Y24.000 F30000\n G1 F1500.000 E0.800\n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 F1500.000 E-0.800\n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos\n G1 Z0.3\n\n G0 X45.000 F30000 ; move to start point\n\nM623 ; end of \"draw extrinsic para cali paint\"\n\nM104 S140\n\n\n;=========== laser and rgb calibration ===========\nM400\nM18 E\nM500 R\n\nM973 S3 P14\n\nG1 X120 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\nT1100\nG1 X235.0 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\nM400 P100\nM960 S1 P1\nM400 P100\nM973 S6 P0; use auto exposure for horizontal laser by xcam\nM960 S0 P0\n\nG1 X240.0 Y6.0 Z0.3 F18000.0;Move to vertical extrude line pos\nM960 S2 P1\nM400 P100\nM973 S6 P1; use auto exposure for vertical laser by xcam\nM960 S0 P0\n\n;=========== handeye calibration ======================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M973 S3 P1 ; camera start stream\n M400 P500\n M973 S1\n G0 F6000 X40.000 Y54.500 Z0.000\n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500\n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1\n G0 Y37.50\n M400 P200\n M971 S5 P1\n M960 S0 P0\n M960 S2 P1\n G0 Y54.50\n M400 P200\n M971 S5 P3\n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1\n G0 Y37.50\n M400 P200\n M971 S5 P2\n M960 S0 P0\n M960 S2 P1\n G0 Y54.50\n M400 P500\n M971 S5 P4\n M963 S1\n M400 P1500\n M964\n T1100\n G0 F6000 X40.000 Y54.500 Z0.000\n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500\n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1\n G0 Y37.50\n M400 P200\n M971 S5 P1\n M960 S0 P0\n M960 S2 P1\n G0 Y54.50\n M400 P200\n M971 S5 P3\n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1\n G0 Y37.50\n M400 P200\n M971 S5 P2\n M960 S0 P0\n M960 S2 P1\n G0 Y54.50\n M400 P500\n M971 S5 P4\n M963 S1\n M400 P1500\n M964\n T1100\n G1 Z3 F3000\n\n M400\n M500 ; save cali data\n\n M104 S{nozzle_temperature[initial_no_support_extruder]} ; rise nozzle temp now ,to reduce temp waiting time.\n\n T1100\n M400 P400\n M960 S0 P0\n G0 F30000.000 Y22.000 X65.000 Z0.000\n M400 P400\n M960 S1 P1\n M400 P50\n\n M969 S1 N3 A2000\n G0 F360.000 X181.000 Z0.000\n M980.3 A70.000 B{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60/4} C5.000 D{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60} E5.000 F175.000 H1.000 I0.000 J0.080 K0.160\n M400 P100\n G0 F20000\n G0 Z1 ; rise nozzle up\n T1000 ; change to nozzle space\n G0 X45.000 Y16.000 F30000 ; move to test line pos\n M969 S0 ; turn off scanning\n M960 S0 P0\n\n\n G1 Z2 F20000\n T1000\n G0 X45.000 Y16.000 F30000 E0\n M109 S{nozzle_temperature[initial_no_support_extruder]}\n G0 Z0.3\n G1 F1500.000 E3.600\n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n\n ; see if extrude cali success, if not ,use default value\n M1002 judge_last_extrude_cali_success\n M622 J0\n M400\n M900 K0.08 M{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*0.08}\n M623\n\n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X185.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X190.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X195.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X200.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X205.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X210.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X215.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X220.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X225.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n M973 S4\n\nM623\n\n;========turn off light and wait extrude temperature =============\nM1002 gcode_claim_action : 0\nM973 S4 ; turn off scanner\nM400 ; wait all motion done before implement the emprical L parameters\n;M900 L500.0 ; Empirical parameters\nM109 S[nozzle_temperature_initial_layer]\nM960 S1 P0 ; turn off laser\nM960 S2 P0 ; turn off laser\nM106 S0 ; turn off fan\nM106 P2 S0 ; turn off big fan\nM106 P3 S0 ; turn off chamber fan\n\nM975 S1 ; turn on mech mode supression\nG90\nM83\nT1000\nG1 E{-retraction_length[initial_no_support_extruder]} F1800\nG1 X128.0 Y253.0 Z0.2 F24000.0;Move to start position\nG1 E{retraction_length[initial_no_support_extruder]} F1800\nM109 S{nozzle_temperature_initial_layer[initial_no_support_extruder]}\nG0 X253 E6.4 F{outer_wall_volumetric_speed/(0.3*0.6) * 60}\nG0 Y128 E6.4\nG0 X252.5\nG0 Y252.5 E6.4\nG0 X128 E6.4", "nozzle_type": "stainless_steel", "upward_compatible_machine":[ "Bambu Lab P1P 0.2 nozzle", diff --git a/resources/profiles/BBL/machine/Bambu Lab X1 0.4 nozzle.json b/resources/profiles/BBL/machine/Bambu Lab X1 0.4 nozzle.json index ea47c288c0..e77c8c8566 100644 --- a/resources/profiles/BBL/machine/Bambu Lab X1 0.4 nozzle.json +++ b/resources/profiles/BBL/machine/Bambu Lab X1 0.4 nozzle.json @@ -26,7 +26,7 @@ "20", "20" ], - "machine_start_gcode": ";===== machine: X1 =========================\n;===== date: 20230130 =====================\n;===== reset machine status =================\nG91\nM17 Z0.4 ; lower the z-motor current\nG0 Z12 F300 ; lower the hotbed , to prevent the nozzle is below the hotbed\nG0 Z-6;\nG90\nM17 X1.2 Y1.2 Z0.75 ; reset motor current to default\nM960 S5 P1 ; turn on logo lamp\nG90\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\nM73.2 R1.0 ;Reset left time magnitude\nM1002 set_gcode_claim_speed_level : 5\nM221 X0 Y0 Z0 ; turn off soft endstop to prevent protential logic problem\nG29.1 Z{+0.0} ; clear z-trim value first\n\n;===== heatbed preheat ====================\nM1002 gcode_claim_action : 2\nM140 S[bed_temperature_initial_layer_single] ;set bed temp\nM190 S[bed_temperature_initial_layer_single] ;wait for bed temp\n\n{if scan_first_layer}\n;=========register first layer scan=====\nM977 S1 P60\n{endif}\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_tool]==\"PLA\"}\n {if (bed_temperature[current_extruder] >45)||(bed_temperature_initial_layer[current_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[current_extruder] >50)||(bed_temperature_initial_layer[current_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n;===== prepare print temperature and material ==========\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp\nG91\nG0 Z10 F1200\nG90\nG28 X\nM975 S1 ; turn on \nG1 X60 F12000\nG1 Y245\nG1 Y265 F3000\nM620 M\nM620 S[initial_tool]A ; switch material if AMS exist\n M109 S[nozzle_temperature_initial_layer]\n G1 X120 F12000\n\n G1 X20 Y50 F12000\n G1 Y-3\n T[initial_tool]\n G1 X54 F12000\n G1 Y265\n M400\nM621 S[initial_tool]A\n\nM412 S1 ; ===turn on filament runout detection===\n\nM109 S250 ;set nozzle to common flush temp\nM106 P1 S0\nG92 E0\nG1 E50 F200\nM400\nM104 S[nozzle_temperature_initial_layer]\nG92 E0\nG1 E50 F200\nM400\nM106 P1 S255\nG92 E0\nG1 E5 F300\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20} ; drop nozzle temp, make filament shink a bit\nG92 E0\nG1 E-0.5 F300\n\nG1 X70 F9000\nG1 X76 F15000\nG1 X65 F15000\nG1 X76 F15000\nG1 X65 F15000; shake to put down garbage\nG1 X80 F6000\nG1 X95 F15000\nG1 X80 F15000\nG1 X165 F15000; wipe and shake\nM400\nM106 P1 S0\n;===== prepare print temperature and material end =====\n\n\n;===== wipe nozzle ===============================\nM1002 gcode_claim_action : 14\nM975 S1\nM106 S255\nG1 X65 Y230 F18000\nG1 Y264 F6000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20}\nG1 X100 F18000 ; first wipe mouth\n\nG0 X135 Y253 F20000 ; move to exposed steel surface edge\nG28 Z P0 T300; home z with low precision,permit 300deg temperature\nG29.2 S0 ; turn off ABL\nG0 Z5 F20000\n\nG1 X60 Y265\nG92 E0\nG1 E-0.5 F300 ; retrack more\nG1 X100 F5000; second wipe mouth\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X90 F5000\nG0 X128 Y261 Z-1.5 F20000 ; move to exposed steel surface and stop the nozzle\nM104 S140 ; set temp down to heatbed acceptable\nM106 S255 ; turn on fan (G28 has turn off fan)\n\nM221 S; push soft endstop status\nM221 Z0 ;turn off Z axis endstop\nG0 Z0.5 F20000\nG0 X125 Y259.5 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 X128\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\n\nM109 S140 ; wait nozzle temp down to heatbed acceptable\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\n\nM221 R; pop softend status\nG1 Z10 F1200\nM400\nG1 Z10\nG1 F30000\nG1 X128 Y128\nG29.2 S1 ; turn on ABL\n;G28 ; home again after hard wipe mouth\nM106 S0 ; turn off fan , too noisy\n;===== wipe nozzle end ================================\n\n;===== check scanner clarity ===========================\nG1 X128 Y128 F24000\nG28 Z P0\nM972 S5 P0\nG1 X230 Y15 F24000\n;===== check scanner clarity end =======================\n\n;===== bed leveling ==================================\nM1002 judge_flag g29_before_print_flag\nM622 J1\n\n M1002 gcode_claim_action : 1\n G29 A X{first_layer_print_min[0]} Y{first_layer_print_min[1]} I{first_layer_print_size[0]} J{first_layer_print_size[1]}\n M400\n M500 ; save cali data\n\nM623\n;===== bed leveling end ================================\n\n;===== home after wipe mouth============================\nM1002 judge_flag g29_before_print_flag\nM622 J0\n\n M1002 gcode_claim_action : 13\n G28\n\nM623\n;===== home after wipe mouth end =======================\n\nM975 S1 ; turn on vibration supression\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_tool]==\"PLA\"}\n {if (bed_temperature[current_extruder] >45)||(bed_temperature_initial_layer[current_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[current_extruder] >50)||(bed_temperature_initial_layer[current_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\nM104 S{nozzle_temperature_initial_layer[initial_extruder]} ; set extrude temp earlier, to reduce wait time\n\n;===== mech mode fast check============================\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q1 A7 B30 C80 H15 K0\nM974 Q1 S2 P0\n\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q0 A7 B30 C90 Q0 H15 K0\nM974 Q0 S2 P0\n\nM975 S1\nG1 F30000\nG1 X230 Y15\nG28 X ; re-home XY \n;===== mech mode fast check============================\n\n{if scan_first_layer}\n;start heatbed scan====================================\nM976 S2 P1 \nG90\nG1 X128 Y128 F20000\nM976 S3 P2 ;register void printing detection\n{endif}\n\n;===== noozle load line ===============================\nM975 S1\nG90 \nM83\nT1000\nG1 X18.0 Y1.0 Z0.8 F18000;Move to start position\nM109 S{nozzle_temperature[initial_extruder]}\nG1 Z0.2\nG0 E2 F300\nG0 X240 E15 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \nG0 Y11 E0.700 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\nG0 X239.5\nG0 E0.2\nG0 Y1.5 E0.700\nG0 X18 E15 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \nM400\n\n;===== for Textured PEI Plate , lower the nozzle as the nozzle was touching topmost of the texture when homing ==\n;curr_bed_type={curr_bed_type}\n{if curr_bed_type==\"Textured PEI Plate\"}\nG29.1 Z{-0.04} ; for Textured PEI Plate\n{endif}\n\n;===== draw extrinsic para cali paint =================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M1002 gcode_claim_action : 8\n\n T1000 \n G0 F3000 X28.000 Y19.500 Z0.200\n G1 F1200.0 X28.000 Y45.000 Z0.200 E0.933 \n G1 F1200.0 X28.500 Y45.000 Z0.200 E0.018 \n G1 F1200.0 X28.500 Y19.500 Z0.200 E0.933 \n G1 F1200.0 X31.000 Y19.500 Z0.200 E0.091 \n G1 F1200.0 X31.000 Y49.000 Z0.200 E1.080 \n G1 F1200.0 X37.500 Y49.000 Z0.200 E0.238 \n G1 F1200.0 X37.500 Y60.000 Z0.200 E0.403 \n G1 F1200.0 X42.500 Y60.000 Z0.200 E0.183 \n G1 F1200.0 X42.500 Y49.000 Z0.200 E0.403 \n G1 F1200.0 X48.000 Y49.000 Z0.200 E0.201 \n G1 F1200.0 X48.000 Y20.000 Z0.200 E1.061 \n G1 F1200.0 X30.000 Y20.000 Z0.200 E0.659 \n G1 F1200.0 X30.000 Y41.000 Z0.200 E0.769 \n G1 F1200.0 X50.000 Y41.000 Z0.200 E0.732 \n G1 F1200.0 X50.000 Y34.000 Z0.200 E0.256 \n G1 F1200.0 X30.000 Y34.000 Z0.200 E0.732 \n G1 F1500.000 E-0.800 \n\n ;=========== extruder cali extrusion ================== \n T1000 \n M83 \n {if default_acceleration > 0}\n {if outer_wall_acceleration > 0}\n M204 S[outer_wall_acceleration]\n {else}\n M204 S[default_acceleration]\n {endif}\n {endif}\n G0 X35.000 Y18.000 Z0.300 F30000 E0\n G1 F1500.000 E0.800 \n M106 S0 ; turn off fan\n G0 X185.000 E9.35441 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G0 X187 Z0\n G1 F1500.000 E-0.800 \n G0 Z1\n G0 X180 Z0.3 F18000\n \n M900 L1000.0 M1.0\n M900 K0.040 \n G0 X45.000 F30000 \n G0 Y20.000 F30000 \n G1 F1500.000 E0.800 \n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 F1500.000 E-0.800 \n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos \n G1 Z0.3\n M400\n\n G0 X45.000 F30000 \n M900 K0.020 \n G0 X45.000 F30000 \n G0 Y22.000 F30000 \n G1 F1500.000 E0.800 \n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 F1500.000 E-0.800 \n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos \n G1 Z0.3\n M400\n\n G0 X45.000 F30000 \n M900 K0.000 \n G0 X45.000 F30000 \n G0 Y24.000 F30000 \n G1 F1500.000 E0.800 \n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 F1500.000 E-0.800\n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos \n G1 Z0.3\n\n G0 X45.000 F30000 ; move to start point\n\nM623 ; end of \"draw extrinsic para cali paint\"\n\nM104 S140\n\n\n;=========== laser and rgb calibration =========== \nM400\nM18 E\nM500 R\n\nM973 S3 P14\n\nG1 X120 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\nT1100\nG1 X143.0 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\n\nM400 P100\n\nM960 S1 P1\nM400 P100\nM973 S6 P0; use auto exposure for horizontal laser by xcam\nM960 S0 P0\n\nG1 X240.0 Y6.0 Z0.3 F18000.0;Move to vertical extrude line pos\nM960 S2 P1\nM400 P100\nM973 S6 P1; use auto exposure for vertical laser by xcam\nM960 S0 P0\n\n;=========== handeye calibration ======================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M973 S3 P1 ; camera start stream\n M400 P500\n M973 S1 \n G0 F6000 X40.000 Y54.500 Z0.000 \n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500 \n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P1 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P200 \n M971 S5 P3 \n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P2 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P500 \n M971 S5 P4 \n M963 S1 \n M400 P1500 \n M964 \n T1100 \n G0 F6000 X40.000 Y54.500 Z0.000 \n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500 \n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P1 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P200 \n M971 S5 P3 \n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P2 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P500 \n M971 S5 P4 \n M963 S1 \n M400 P1500 \n M964 \n T1100 \n G1 Z3 F3000 \n\n M400\n M500 ; save cali data\n\n M104 S{nozzle_temperature[initial_extruder]} ; rise nozzle temp now ,to reduce temp waiting time.\n\n T1100 \n M400 P400 \n M960 S0 P0\n G0 F30000.000 Y22.000 X65.000 Z0.000\n M400 P400 \n M960 S1 P1 \n M400 P50 \n\n M969 S1 N3 A2000 \n G0 F360.000 X181.000 Z0.000\n M980.3 A70.000 B{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60/4} C5.000 D{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60} E5.000 F175.000 H1.000 I0.000 J0.020 K0.040\n M400 P100 \n G0 F20000\n G0 Z1 ; rise nozzle up\n T1000 ; change to nozzle space\n G0 X45.000 Y16.000 F30000 ; move to test line pos\n M969 S0 ; turn off scanning\n M960 S0 P0\n\n\n G1 Z2 F20000 \n T1000 \n G0 X45.000 Y16.000 F30000 E0\n M109 S{nozzle_temperature[initial_extruder]}\n G0 Z0.3\n G1 F1500.000 E3.600 \n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n\n ; see if extrude cali success, if not ,use default value\n M1002 judge_last_extrude_cali_success\n M622 J0\n M400\n M900 K0.02 M{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*0.02}\n M623 \n\n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X185.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X190.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X195.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X200.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X205.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X210.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X215.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X220.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X225.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n M973 S4 \n\nM623\n\n;========turn off light and wait extrude temperature =============\nM1002 gcode_claim_action : 0\nM973 S4 ; turn off scanner\nM400 ; wait all motion done before implement the emprical L parameters\n;M900 L500.0 ; Empirical parameters\nM109 S[nozzle_temperature_initial_layer]\nM960 S1 P0 ; turn off laser\nM960 S2 P0 ; turn off laser\nM106 S0 ; turn off fan\nM106 P2 S0 ; turn off big fan \nM106 P3 S0 ; turn off chamber fan\n\nM975 S1 ; turn on mech mode supression\nG90 \nM83\nT1000\nG1 E{-retraction_length[initial_extruder]} F1800\nG1 X128.0 Y253.0 Z0.2 F24000.0;Move to start position\nG1 E{retraction_length[initial_extruder]} F1800\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG0 X253 E6.4 F{outer_wall_volumetric_speed/(0.3*0.6) * 60} \nG0 Y128 E6.4\nG0 X252.5\nG0 Y252.5 E6.4\nG0 X128 E6.4", + "machine_start_gcode": ";===== machine: X1 =========================\n;===== date: 20230322 =====================\n;===== turn on the HB fan =================\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp to turn on the HB fan\n;===== reset machine status =================\nG91\nM17 Z0.4 ; lower the z-motor current\nG0 Z12 F300 ; lower the hotbed , to prevent the nozzle is below the hotbed\nG0 Z-6;\nG90\nM17 X1.2 Y1.2 Z0.75 ; reset motor current to default\nM960 S5 P1 ; turn on logo lamp\nG90\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\nM73.2 R1.0 ;Reset left time magnitude\nM1002 set_gcode_claim_speed_level : 5\nM221 X0 Y0 Z0 ; turn off soft endstop to prevent protential logic problem\nG29.1 Z{+0.0} ; clear z-trim value first\n\n;===== heatbed preheat ====================\nM1002 gcode_claim_action : 2\nM140 S[bed_temperature_initial_layer_single] ;set bed temp\nM190 S[bed_temperature_initial_layer_single] ;wait for bed temp\n\n{if scan_first_layer}\n;=========register first layer scan=====\nM977 S1 P60\n{endif}\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_no_support_extruder]==\"PLA\"}\n {if (bed_temperature[initial_no_support_extruder] >45)||(bed_temperature_initial_layer[initial_no_support_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[initial_no_support_extruder] >50)||(bed_temperature_initial_layer[initial_no_support_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n;===== prepare print temperature and material ==========\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp\nG91\nG0 Z10 F1200\nG90\nG28 X\nM975 S1 ; turn on \nG1 X60 F12000\nG1 Y245\nG1 Y265 F3000\nM620 M\nM620 S[initial_no_support_extruder]A ; switch material if AMS exist\n M109 S[nozzle_temperature_initial_layer]\n G1 X120 F12000\n\n G1 X20 Y50 F12000\n G1 Y-3\n T[initial_no_support_extruder]\n G1 X54 F12000\n G1 Y265\n M400\nM621 S[initial_no_support_extruder]A\nM620.1 E F{filament_max_volumetric_speed[initial_no_support_extruder]/2.4053*60} T{nozzle_temperature_range_high[initial_no_support_extruder]}\n\nM412 S1 ; ===turn on filament runout detection===\n\nM109 S250 ;set nozzle to common flush temp\nM106 P1 S0\nG92 E0\nG1 E50 F200\nM400\nM104 S[nozzle_temperature_initial_layer]\nG92 E0\nG1 E50 F200\nM400\nM106 P1 S255\nG92 E0\nG1 E5 F300\nM109 S{nozzle_temperature_initial_layer[initial_no_support_extruder]-20} ; drop nozzle temp, make filament shink a bit\nG92 E0\nG1 E-0.5 F300\n\nG1 X70 F9000\nG1 X76 F15000\nG1 X65 F15000\nG1 X76 F15000\nG1 X65 F15000; shake to put down garbage\nG1 X80 F6000\nG1 X95 F15000\nG1 X80 F15000\nG1 X165 F15000; wipe and shake\nM400\nM106 P1 S0\n;===== prepare print temperature and material end =====\n\n\n;===== wipe nozzle ===============================\nM1002 gcode_claim_action : 14\nM975 S1\nM106 S255\nG1 X65 Y230 F18000\nG1 Y264 F6000\nM109 S{nozzle_temperature_initial_layer[initial_no_support_extruder]-20}\nG1 X100 F18000 ; first wipe mouth\n\nG0 X135 Y253 F20000 ; move to exposed steel surface edge\nG28 Z P0 T300; home z with low precision,permit 300deg temperature\nG29.2 S0 ; turn off ABL\nG0 Z5 F20000\n\nG1 X60 Y265\nG92 E0\nG1 E-0.5 F300 ; retrack more\nG1 X100 F5000; second wipe mouth\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X90 F5000\nG0 X128 Y261 Z-1.5 F20000 ; move to exposed steel surface and stop the nozzle\nM104 S140 ; set temp down to heatbed acceptable\nM106 S255 ; turn on fan (G28 has turn off fan)\n\nM221 S; push soft endstop status\nM221 Z0 ;turn off Z axis endstop\nG0 Z0.5 F20000\nG0 X125 Y259.5 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 X128\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\n\nM109 S140 ; wait nozzle temp down to heatbed acceptable\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\n\nM221 R; pop softend status\nG1 Z10 F1200\nM400\nG1 Z10\nG1 F30000\nG1 X128 Y128\nG29.2 S1 ; turn on ABL\n;G28 ; home again after hard wipe mouth\nM106 S0 ; turn off fan , too noisy\n;===== wipe nozzle end ================================\n\n;===== check scanner clarity ===========================\nG1 X128 Y128 F24000\nG28 Z P0\nM972 S5 P0\nG1 X230 Y15 F24000\n;===== check scanner clarity end =======================\n\n;===== bed leveling ==================================\nM1002 judge_flag g29_before_print_flag\nM622 J1\n\n M1002 gcode_claim_action : 1\n G29 A X{first_layer_print_min[0]} Y{first_layer_print_min[1]} I{first_layer_print_size[0]} J{first_layer_print_size[1]}\n M400\n M500 ; save cali data\n\nM623\n;===== bed leveling end ================================\n\n;===== home after wipe mouth============================\nM1002 judge_flag g29_before_print_flag\nM622 J0\n\n M1002 gcode_claim_action : 13\n G28\n\nM623\n;===== home after wipe mouth end =======================\n\nM975 S1 ; turn on vibration supression\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_no_support_extruder]==\"PLA\"}\n {if (bed_temperature[initial_no_support_extruder] >45)||(bed_temperature_initial_layer[initial_no_support_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[initial_no_support_extruder] >50)||(bed_temperature_initial_layer[initial_no_support_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\nM104 S{nozzle_temperature_initial_layer[initial_no_support_extruder]} ; set extrude temp earlier, to reduce wait time\n\n;===== mech mode fast check============================\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q1 A7 B30 C80 H15 K0\nM974 Q1 S2 P0\n\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q0 A7 B30 C90 Q0 H15 K0\nM974 Q0 S2 P0\n\nM975 S1\nG1 F30000\nG1 X230 Y15\nG28 X ; re-home XY \n;===== mech mode fast check============================\n\n{if scan_first_layer}\n;start heatbed scan====================================\nM976 S2 P1 \nG90\nG1 X128 Y128 F20000\nM976 S3 P2 ;register void printing detection\n{endif}\n\n;===== noozle load line ===============================\nM975 S1\nG90 \nM83\nT1000\nG1 X18.0 Y1.0 Z0.8 F18000;Move to start position\nM109 S{nozzle_temperature[initial_no_support_extruder]}\nG1 Z0.2\nG0 E2 F300\nG0 X240 E15 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \nG0 Y11 E0.700 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\nG0 X239.5\nG0 E0.2\nG0 Y1.5 E0.700\nG0 X18 E15 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \nM400\n\n;===== for Textured PEI Plate , lower the nozzle as the nozzle was touching topmost of the texture when homing ==\n;curr_bed_type={curr_bed_type}\n{if curr_bed_type==\"Textured PEI Plate\"}\nG29.1 Z{-0.04} ; for Textured PEI Plate\n{endif}\n\n;===== draw extrinsic para cali paint =================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M1002 gcode_claim_action : 8\n\n T1000 \n G0 F3000 X28.000 Y19.500 Z0.200\n G1 F1200.0 X28.000 Y45.000 Z0.200 E0.933 \n G1 F1200.0 X28.500 Y45.000 Z0.200 E0.018 \n G1 F1200.0 X28.500 Y19.500 Z0.200 E0.933 \n G1 F1200.0 X31.000 Y19.500 Z0.200 E0.091 \n G1 F1200.0 X31.000 Y49.000 Z0.200 E1.080 \n G1 F1200.0 X37.500 Y49.000 Z0.200 E0.238 \n G1 F1200.0 X37.500 Y60.000 Z0.200 E0.403 \n G1 F1200.0 X42.500 Y60.000 Z0.200 E0.183 \n G1 F1200.0 X42.500 Y49.000 Z0.200 E0.403 \n G1 F1200.0 X48.000 Y49.000 Z0.200 E0.201 \n G1 F1200.0 X48.000 Y20.000 Z0.200 E1.061 \n G1 F1200.0 X30.000 Y20.000 Z0.200 E0.659 \n G1 F1200.0 X30.000 Y41.000 Z0.200 E0.769 \n G1 F1200.0 X50.000 Y41.000 Z0.200 E0.732 \n G1 F1200.0 X50.000 Y34.000 Z0.200 E0.256 \n G1 F1200.0 X30.000 Y34.000 Z0.200 E0.732 \n G1 F1500.000 E-0.800 \n\n ;=========== extruder cali extrusion ================== \n T1000 \n M83 \n {if default_acceleration > 0}\n {if outer_wall_acceleration > 0}\n M204 S[outer_wall_acceleration]\n {else}\n M204 S[default_acceleration]\n {endif}\n {endif}\n G0 X35.000 Y18.000 Z0.300 F30000 E0\n G1 F1500.000 E0.800 \n M106 S0 ; turn off fan\n G0 X185.000 E9.35441 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G0 X187 Z0\n G1 F1500.000 E-0.800 \n G0 Z1\n G0 X180 Z0.3 F18000\n \n M900 L1000.0 M1.0\n M900 K0.040 \n G0 X45.000 F30000 \n G0 Y20.000 F30000 \n G1 F1500.000 E0.800 \n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 F1500.000 E-0.800 \n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos \n G1 Z0.3\n M400\n\n G0 X45.000 F30000 \n M900 K0.020 \n G0 X45.000 F30000 \n G0 Y22.000 F30000 \n G1 F1500.000 E0.800 \n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 F1500.000 E-0.800 \n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos \n G1 Z0.3\n M400\n\n G0 X45.000 F30000 \n M900 K0.000 \n G0 X45.000 F30000 \n G0 Y24.000 F30000 \n G1 F1500.000 E0.800 \n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 F1500.000 E-0.800\n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos \n G1 Z0.3\n\n G0 X45.000 F30000 ; move to start point\n\nM623 ; end of \"draw extrinsic para cali paint\"\n\nM104 S140\n\n\n;=========== laser and rgb calibration =========== \nM400\nM18 E\nM500 R\n\nM973 S3 P14\n\nG1 X120 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\nT1100\nG1 X235.0 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\nM400 P100\nM960 S1 P1\nM400 P100\nM973 S6 P0; use auto exposure for horizontal laser by xcam\nM960 S0 P0\n\nG1 X240.0 Y6.0 Z0.3 F18000.0;Move to vertical extrude line pos\nM960 S2 P1\nM400 P100\nM973 S6 P1; use auto exposure for vertical laser by xcam\nM960 S0 P0\n\n;=========== handeye calibration ======================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M973 S3 P1 ; camera start stream\n M400 P500\n M973 S1 \n G0 F6000 X40.000 Y54.500 Z0.000 \n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500 \n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P1 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P200 \n M971 S5 P3 \n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P2 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P500 \n M971 S5 P4 \n M963 S1 \n M400 P1500 \n M964 \n T1100 \n G0 F6000 X40.000 Y54.500 Z0.000 \n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500 \n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P1 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P200 \n M971 S5 P3 \n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P2 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P500 \n M971 S5 P4 \n M963 S1 \n M400 P1500 \n M964 \n T1100 \n G1 Z3 F3000 \n\n M400\n M500 ; save cali data\n\n M104 S{nozzle_temperature[initial_no_support_extruder]} ; rise nozzle temp now ,to reduce temp waiting time.\n\n T1100 \n M400 P400 \n M960 S0 P0\n G0 F30000.000 Y22.000 X65.000 Z0.000\n M400 P400 \n M960 S1 P1 \n M400 P50 \n\n M969 S1 N3 A2000 \n G0 F360.000 X181.000 Z0.000\n M980.3 A70.000 B{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60/4} C5.000 D{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60} E5.000 F175.000 H1.000 I0.000 J0.020 K0.040\n M400 P100 \n G0 F20000\n G0 Z1 ; rise nozzle up\n T1000 ; change to nozzle space\n G0 X45.000 Y16.000 F30000 ; move to test line pos\n M969 S0 ; turn off scanning\n M960 S0 P0\n\n\n G1 Z2 F20000 \n T1000 \n G0 X45.000 Y16.000 F30000 E0\n M109 S{nozzle_temperature[initial_no_support_extruder]}\n G0 Z0.3\n G1 F1500.000 E3.600 \n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n\n ; see if extrude cali success, if not ,use default value\n M1002 judge_last_extrude_cali_success\n M622 J0\n M400\n M900 K0.02 M{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*0.02}\n M623 \n\n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X185.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X190.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X195.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X200.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X205.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X210.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X215.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X220.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X225.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n M973 S4 \n\nM623\n\n;========turn off light and wait extrude temperature =============\nM1002 gcode_claim_action : 0\nM973 S4 ; turn off scanner\nM400 ; wait all motion done before implement the emprical L parameters\n;M900 L500.0 ; Empirical parameters\nM109 S[nozzle_temperature_initial_layer]\nM960 S1 P0 ; turn off laser\nM960 S2 P0 ; turn off laser\nM106 S0 ; turn off fan\nM106 P2 S0 ; turn off big fan \nM106 P3 S0 ; turn off chamber fan\n\nM975 S1 ; turn on mech mode supression\nG90 \nM83\nT1000\nG1 E{-retraction_length[initial_no_support_extruder]} F1800\nG1 X128.0 Y253.0 Z0.2 F24000.0;Move to start position\nG1 E{retraction_length[initial_no_support_extruder]} F1800\nM109 S{nozzle_temperature_initial_layer[initial_no_support_extruder]}\nG0 X253 E6.4 F{outer_wall_volumetric_speed/(0.3*0.6) * 60} \nG0 Y128 E6.4\nG0 X252.5\nG0 Y252.5 E6.4\nG0 X128 E6.4", "scan_first_layer": "1", "machine_load_filament_time": "29", "machine_unload_filament_time": "28", diff --git a/resources/profiles/BBL/machine/Bambu Lab X1 0.6 nozzle.json b/resources/profiles/BBL/machine/Bambu Lab X1 0.6 nozzle.json index fd45149256..bfc3bfa467 100644 --- a/resources/profiles/BBL/machine/Bambu Lab X1 0.6 nozzle.json +++ b/resources/profiles/BBL/machine/Bambu Lab X1 0.6 nozzle.json @@ -26,7 +26,7 @@ "retraction_minimum_travel": [ "3" ], - "machine_start_gcode": ";===== machine: X1 =========================\n;===== date: 20230130 =====================\n;===== reset machine status =================\nG91\nM17 Z0.4 ; lower the z-motor current\nG0 Z12 F300 ; lower the hotbed , to prevent the nozzle is below the hotbed\nG0 Z-6;\nG90\nM17 X1.2 Y1.2 Z0.75 ; reset motor current to default\nM960 S5 P1 ; turn on logo lamp\nG90\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\nM73.2 R1.0 ;Reset left time magnitude\nM1002 set_gcode_claim_speed_level : 5\nM221 X0 Y0 Z0 ; turn off soft endstop to prevent protential logic problem\nG29.1 Z{+0.0} ; clear z-trim value first\n\n;===== heatbed preheat ====================\nM1002 gcode_claim_action : 2\nM140 S[bed_temperature_initial_layer_single] ;set bed temp\nM190 S[bed_temperature_initial_layer_single] ;wait for bed temp\n\n{if scan_first_layer}\n;=========register first layer scan=====\nM977 S1 P60\n{endif}\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_tool]==\"PLA\"}\n {if (bed_temperature[current_extruder] >45)||(bed_temperature_initial_layer[current_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[current_extruder] >50)||(bed_temperature_initial_layer[current_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n;===== prepare print temperature and material ==========\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp\nG91\nG0 Z10 F1200\nG90\nG28 X\nM975 S1 ; turn on \nG1 X60 F12000\nG1 Y245\nG1 Y265 F3000\nM620 M\nM620 S[initial_tool]A ; switch material if AMS exist\n M109 S[nozzle_temperature_initial_layer]\n G1 X120 F12000\n\n G1 X20 Y50 F12000\n G1 Y-3\n T[initial_tool]\n G1 X54 F12000\n G1 Y265\n M400\nM621 S[initial_tool]A\n\nM412 S1 ; ===turn on filament runout detection===\n\nM109 S250 ;set nozzle to common flush temp\nM106 P1 S0\nG92 E0\nG1 E50 F200\nM400\nM104 S[nozzle_temperature_initial_layer]\nG92 E0\nG1 E50 F200\nM400\nM106 P1 S255\nG92 E0\nG1 E5 F300\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20} ; drop nozzle temp, make filament shink a bit\nG92 E0\nG1 E-0.5 F300\n\nG1 X70 F9000\nG1 X76 F15000\nG1 X65 F15000\nG1 X76 F15000\nG1 X65 F15000; shake to put down garbage\nG1 X80 F6000\nG1 X95 F15000\nG1 X80 F15000\nG1 X165 F15000; wipe and shake\nM400\nM106 P1 S0\n;===== prepare print temperature and material end =====\n\n\n;===== wipe nozzle ===============================\nM1002 gcode_claim_action : 14\nM975 S1\nM106 S255\nG1 X65 Y230 F18000\nG1 Y264 F6000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20}\nG1 X100 F18000 ; first wipe mouth\n\nG0 X135 Y253 F20000 ; move to exposed steel surface edge\nG28 Z P0 T300; home z with low precision,permit 300deg temperature\nG29.2 S0 ; turn off ABL\nG0 Z5 F20000\n\nG1 X60 Y265\nG92 E0\nG1 E-0.5 F300 ; retrack more\nG1 X100 F5000; second wipe mouth\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X90 F5000\nG0 X128 Y261 Z-1.5 F20000 ; move to exposed steel surface and stop the nozzle\nM104 S140 ; set temp down to heatbed acceptable\nM106 S255 ; turn on fan (G28 has turn off fan)\n\nM221 S; push soft endstop status\nM221 Z0 ;turn off Z axis endstop\nG0 Z0.5 F20000\nG0 X125 Y259.5 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 X128\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\n\nM109 S140 ; wait nozzle temp down to heatbed acceptable\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\n\nM221 R; pop softend status\nG1 Z10 F1200\nM400\nG1 Z10\nG1 F30000\nG1 X128 Y128\nG29.2 S1 ; turn on ABL\n;G28 ; home again after hard wipe mouth\nM106 S0 ; turn off fan , too noisy\n;===== wipe nozzle end ================================\n\n;===== check scanner clarity ===========================\nG1 X128 Y128 F24000\nG28 Z P0\nM972 S5 P0\nG1 X230 Y15 F24000\n;===== check scanner clarity end =======================\n\n;===== bed leveling ==================================\nM1002 judge_flag g29_before_print_flag\nM622 J1\n\n M1002 gcode_claim_action : 1\n G29 A X{first_layer_print_min[0]} Y{first_layer_print_min[1]} I{first_layer_print_size[0]} J{first_layer_print_size[1]}\n M400\n M500 ; save cali data\n\nM623\n;===== bed leveling end ================================\n\n;===== home after wipe mouth============================\nM1002 judge_flag g29_before_print_flag\nM622 J0\n\n M1002 gcode_claim_action : 13\n G28\n\nM623\n;===== home after wipe mouth end =======================\n\nM975 S1 ; turn on vibration supression\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_tool]==\"PLA\"}\n {if (bed_temperature[current_extruder] >45)||(bed_temperature_initial_layer[current_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[current_extruder] >50)||(bed_temperature_initial_layer[current_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\nM104 S{nozzle_temperature_initial_layer[initial_extruder]} ; set extrude temp earlier, to reduce wait time\n\n;===== mech mode fast check============================\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q1 A7 B30 C80 H15 K0\nM974 Q1 S2 P0\n\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q0 A7 B30 C90 Q0 H15 K0\nM974 Q0 S2 P0\n\nM975 S1\nG1 F30000\nG1 X230 Y15\nG28 X ; re-home XY \n;===== mech mode fast check============================\n\n{if scan_first_layer}\n;start heatbed scan====================================\nM976 S2 P1 \nG90\nG1 X128 Y128 F20000\nM976 S3 P2 ;register void printing detection\n{endif}\n\n;===== noozle load line ===============================\nM975 S1\nG90 \nM83\nT1000\nG1 X18.0 Y1.0 Z0.8 F18000;Move to start position\nM109 S{nozzle_temperature[initial_extruder]}\nG1 Z0.2\nG0 E2 F300\nG0 X240 E25 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \nG0 Y15 E1.166 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\nG0 X239.5\nG0 E0.2\nG0 Y1.5 E1.166\nG0 X18 E25 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \nM400\n\n;===== for Textured PEI Plate , lower the nozzle as the nozzle was touching topmost of the texture when homing ==\n;curr_bed_type={curr_bed_type}\n{if curr_bed_type==\"Textured PEI Plate\"}\nG29.1 Z{-0.04} ; for Textured PEI Plate\n{endif}\n\n;===== draw extrinsic para cali paint =================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M1002 gcode_claim_action : 8\n\n T1000 \n G0 F3000 X28.000 Y19.500 Z0.200\n G1 F1200.0 X28.000 Y45.000 Z0.200 E1.679 \n G1 F1200.0 X28.500 Y45.000 Z0.200 E0.032 \n G1 F1200.0 X28.500 Y19.500 Z0.200 E1.679 \n G1 F1200.0 X31.000 Y19.500 Z0.200 E0.164 \n G1 F1200.0 X31.000 Y49.000 Z0.200 E1.944 \n G1 F1200.0 X37.500 Y49.000 Z0.200 E0.428 \n G1 F1200.0 X37.500 Y60.000 Z0.200 E0.725 \n G1 F1200.0 X42.500 Y60.000 Z0.200 E0.329 \n G1 F1200.0 X42.500 Y49.000 Z0.200 E0.725 \n G1 F1200.0 X48.000 Y49.000 Z0.200 E0.362 \n G1 F1200.0 X48.000 Y20.000 Z0.200 E1.910 \n G1 F1200.0 X30.000 Y20.000 Z0.200 E1.186 \n G1 F1200.0 X30.000 Y41.000 Z0.200 E1.384 \n G1 F1200.0 X50.000 Y41.000 Z0.200 E1.318 \n G1 F1200.0 X50.000 Y34.000 Z0.200 E0.461 \n G1 F1200.0 X30.000 Y34.000 Z0.200 E1.318 \n G1 F1500.000 E-0.800 \n\n ;=========== extruder cali extrusion ================== \n T1000 \n M83 \n {if default_acceleration > 0}\n {if outer_wall_acceleration > 0}\n M204 S[outer_wall_acceleration]\n {else}\n M204 S[default_acceleration]\n {endif}\n {endif}\n G0 X35.000 Y18.000 Z0.300 F30000 E0\n G1 F1500.000 E0.800 \n M106 S0 ; turn off fan\n G0 X185.000 E16.9 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G0 X187 Z0\n G1 F1500.000 E-0.800 \n G0 Z1\n G0 X180 Z0.3 F18000\n \n M900 L1000.0 M1.0\n M900 K0.030 \n G0 X45.000 F30000 \n G0 Y20.000 F30000 \n G1 F1500.000 E0.800 \n G1 X65.000 E2.25000 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X70.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X75.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X80.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X85.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X90.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X95.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X100.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X105.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X110.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X115.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X120.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X125.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X130.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X135.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X140.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X145.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X150.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X155.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X160.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X165.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X170.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X175.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X180.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 F1500.000 E-0.800 \n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos \n G1 Z0.3\n M400\n\n G0 X45.000 F30000 \n M900 K0.015 \n G0 X45.000 F30000 \n G0 Y22.000 F30000 \n G1 F1500.000 E0.800 \n G1 X65.000 E2.25000 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X70.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X75.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X80.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X85.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X90.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X95.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X100.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X105.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X110.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X115.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X120.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X125.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X130.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X135.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X140.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X145.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X150.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X155.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X160.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X165.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X170.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X175.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X180.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 F1500.000 E-0.800 \n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos \n G1 Z0.3\n M400\n\n G0 X45.000 F30000 \n M900 K0.000 \n G0 X45.000 F30000 \n G0 Y24.000 F30000 \n G1 F1500.000 E0.800 \n G1 X65.000 E2.25000 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X70.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X75.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X80.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X85.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X90.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X95.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X100.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X105.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X110.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X115.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X120.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X125.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X130.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X135.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X140.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X145.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X150.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X155.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X160.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X165.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X170.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X175.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X180.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 F1500.000 E-0.800\n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos \n G1 Z0.3\n\n G0 X45.000 F30000 ; move to start point\n\nM623 ; end of \"draw extrinsic para cali paint\"\n\nM104 S140\n\n\n;=========== laser and rgb calibration =========== \nM400\nM18 E\nM500 R\n\nM973 S3 P14\n\nG1 X120 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\nT1100\nG1 X143.0 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\n\nM400 P100\n\nM960 S1 P1\nM400 P100\nM973 S6 P0; use auto exposure for horizontal laser by xcam\nM960 S0 P0\n\nG1 X240.0 Y6.0 Z0.3 F18000.0;Move to vertical extrude line pos\nM960 S2 P1\nM400 P100\nM973 S6 P1; use auto exposure for vertical laser by xcam\nM960 S0 P0\n\n;=========== handeye calibration ======================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M973 S3 P1 ; camera start stream\n M400 P500\n M973 S1 \n G0 F6000 X40.000 Y54.500 Z0.000 \n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500 \n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P1 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P200 \n M971 S5 P3 \n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P2 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P500 \n M971 S5 P4 \n M963 S1 \n M400 P1500 \n M964 \n T1100 \n G0 F6000 X40.000 Y54.500 Z0.000 \n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500 \n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P1 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P200 \n M971 S5 P3 \n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P2 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P500 \n M971 S5 P4 \n M963 S1 \n M400 P1500 \n M964 \n T1100 \n G1 Z3 F3000 \n\n M400\n M500 ; save cali data\n\n M104 S{nozzle_temperature[initial_extruder]} ; rise nozzle temp now ,to reduce temp waiting time.\n\n T1100 \n M400 P400 \n M960 S0 P0\n G0 F30000.000 Y22.000 X65.000 Z0.000\n M400 P400 \n M960 S1 P1 \n M400 P50 \n\n M969 S1 N3 A2000 \n G0 F360.000 X181.000 Z0.000\n M980.3 A70.000 B{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60/4} C5.000 D{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60} E5.000 F175.000 H1.000 I0.000 J0.015 K0.030\n M400 P100 \n G0 F20000\n G0 Z1 ; rise nozzle up\n T1000 ; change to nozzle space\n G0 X45.000 Y16.000 F30000 ; move to test line pos\n M969 S0 ; turn off scanning\n M960 S0 P0\n\n\n G1 Z2 F20000 \n T1000 \n G0 X45.000 Y16.000 F30000 E0\n M109 S{nozzle_temperature[initial_extruder]}\n G0 Z0.3\n G1 F1500.000 E3.600 \n G1 X65.000 E2.25000 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X75.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X80.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X85.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X90.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X95.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X100.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X105.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X110.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X115.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X120.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X125.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X130.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X135.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n\n ; see if extrude cali success, if not ,use default value\n M1002 judge_last_extrude_cali_success\n M622 J0\n M400\n M900 K0.015 M{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*0.015}\n M623 \n\n G1 X140.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X145.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X150.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X155.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X160.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X165.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X170.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X175.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X180.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X185.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X190.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X195.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X200.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X205.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X210.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X215.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X220.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X225.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n M973 S4 \n\nM623\n\n;========turn off light and wait extrude temperature =============\nM1002 gcode_claim_action : 0\nM973 S4 ; turn off scanner\nM400 ; wait all motion done before implement the emprical L parameters\n;M900 L500.0 ; Empirical parameters\nM109 S[nozzle_temperature_initial_layer]\nM960 S1 P0 ; turn off laser\nM960 S2 P0 ; turn off laser\nM106 S0 ; turn off fan\nM106 P2 S0 ; turn off big fan \nM106 P3 S0 ; turn off chamber fan\n\nM975 S1 ; turn on mech mode supression\nG90 \nM83\nT1000\nG1 E{-retraction_length[initial_extruder]} F1800\nG1 X128.0 Y253.0 Z0.2 F24000.0;Move to start position\nG1 E{retraction_length[initial_extruder]} F1800\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG0 X253 E6.4 F{outer_wall_volumetric_speed/(0.3*0.6) * 60} \nG0 Y128 E6.4\nG0 X252.5\nG0 Y252.5 E6.4\nG0 X128 E6.4", + "machine_start_gcode": ";===== machine: X1 =========================\n;===== date: 20230322 =====================\n;===== turn on the HB fan =================\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp to turn on the HB fan\n;===== reset machine status =================\nG91\nM17 Z0.4 ; lower the z-motor current\nG0 Z12 F300 ; lower the hotbed , to prevent the nozzle is below the hotbed\nG0 Z-6;\nG90\nM17 X1.2 Y1.2 Z0.75 ; reset motor current to default\nM960 S5 P1 ; turn on logo lamp\nG90\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\nM73.2 R1.0 ;Reset left time magnitude\nM1002 set_gcode_claim_speed_level : 5\nM221 X0 Y0 Z0 ; turn off soft endstop to prevent protential logic problem\nG29.1 Z{+0.0} ; clear z-trim value first\n\n;===== heatbed preheat ====================\nM1002 gcode_claim_action : 2\nM140 S[bed_temperature_initial_layer_single] ;set bed temp\nM190 S[bed_temperature_initial_layer_single] ;wait for bed temp\n\n{if scan_first_layer}\n;=========register first layer scan=====\nM977 S1 P60\n{endif}\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_no_support_extruder]==\"PLA\"}\n {if (bed_temperature[initial_no_support_extruder] >45)||(bed_temperature_initial_layer[initial_no_support_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[initial_no_support_extruder] >50)||(bed_temperature_initial_layer[initial_no_support_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n;===== prepare print temperature and material ==========\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp\nG91\nG0 Z10 F1200\nG90\nG28 X\nM975 S1 ; turn on\nG1 X60 F12000\nG1 Y245\nG1 Y265 F3000\nM620 M\nM620 S[initial_no_support_extruder]A ; switch material if AMS exist\n M109 S[nozzle_temperature_initial_layer]\n G1 X120 F12000\n\n G1 X20 Y50 F12000\n G1 Y-3\n T[initial_no_support_extruder]\n G1 X54 F12000\n G1 Y265\n M400\nM621 S[initial_no_support_extruder]A\nM620.1 E F{filament_max_volumetric_speed[initial_no_support_extruder]/2.4053*60} T{nozzle_temperature_range_high[initial_no_support_extruder]}\n\n\nM412 S1 ; ===turn on filament runout detection===\n\nM109 S250 ;set nozzle to common flush temp\nM106 P1 S0\nG92 E0\nG1 E50 F200\nM400\nM104 S[nozzle_temperature_initial_layer]\nG92 E0\nG1 E50 F200\nM400\nM106 P1 S255\nG92 E0\nG1 E5 F300\nM109 S{nozzle_temperature_initial_layer[initial_no_support_extruder]-20} ; drop nozzle temp, make filament shink a bit\nG92 E0\nG1 E-0.5 F300\n\nG1 X70 F9000\nG1 X76 F15000\nG1 X65 F15000\nG1 X76 F15000\nG1 X65 F15000; shake to put down garbage\nG1 X80 F6000\nG1 X95 F15000\nG1 X80 F15000\nG1 X165 F15000; wipe and shake\nM400\nM106 P1 S0\n;===== prepare print temperature and material end =====\n\n\n;===== wipe nozzle ===============================\nM1002 gcode_claim_action : 14\nM975 S1\nM106 S255\nG1 X65 Y230 F18000\nG1 Y264 F6000\nM109 S{nozzle_temperature_initial_layer[initial_no_support_extruder]-20}\nG1 X100 F18000 ; first wipe mouth\n\nG0 X135 Y253 F20000 ; move to exposed steel surface edge\nG28 Z P0 T300; home z with low precision,permit 300deg temperature\nG29.2 S0 ; turn off ABL\nG0 Z5 F20000\n\nG1 X60 Y265\nG92 E0\nG1 E-0.5 F300 ; retrack more\nG1 X100 F5000; second wipe mouth\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X90 F5000\nG0 X128 Y261 Z-1.5 F20000 ; move to exposed steel surface and stop the nozzle\nM104 S140 ; set temp down to heatbed acceptable\nM106 S255 ; turn on fan (G28 has turn off fan)\n\nM221 S; push soft endstop status\nM221 Z0 ;turn off Z axis endstop\nG0 Z0.5 F20000\nG0 X125 Y259.5 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 X128\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\n\nM109 S140 ; wait nozzle temp down to heatbed acceptable\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\n\nM221 R; pop softend status\nG1 Z10 F1200\nM400\nG1 Z10\nG1 F30000\nG1 X128 Y128\nG29.2 S1 ; turn on ABL\n;G28 ; home again after hard wipe mouth\nM106 S0 ; turn off fan , too noisy\n;===== wipe nozzle end ================================\n\n;===== check scanner clarity ===========================\nG1 X128 Y128 F24000\nG28 Z P0\nM972 S5 P0\nG1 X230 Y15 F24000\n;===== check scanner clarity end =======================\n\n;===== bed leveling ==================================\nM1002 judge_flag g29_before_print_flag\nM622 J1\n\n M1002 gcode_claim_action : 1\n G29 A X{first_layer_print_min[0]} Y{first_layer_print_min[1]} I{first_layer_print_size[0]} J{first_layer_print_size[1]}\n M400\n M500 ; save cali data\n\nM623\n;===== bed leveling end ================================\n\n;===== home after wipe mouth============================\nM1002 judge_flag g29_before_print_flag\nM622 J0\n\n M1002 gcode_claim_action : 13\n G28\n\nM623\n;===== home after wipe mouth end =======================\n\nM975 S1 ; turn on vibration supression\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_no_support_extruder]==\"PLA\"}\n {if (bed_temperature[initial_no_support_extruder] >45)||(bed_temperature_initial_layer[initial_no_support_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[initial_no_support_extruder] >50)||(bed_temperature_initial_layer[initial_no_support_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\nM104 S{nozzle_temperature_initial_layer[initial_no_support_extruder]} ; set extrude temp earlier, to reduce wait time\n\n;===== mech mode fast check============================\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q1 A7 B30 C80 H15 K0\nM974 Q1 S2 P0\n\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q0 A7 B30 C90 Q0 H15 K0\nM974 Q0 S2 P0\n\nM975 S1\nG1 F30000\nG1 X230 Y15\nG28 X ; re-home XY\n;===== mech mode fast check============================\n\n{if scan_first_layer}\n;start heatbed scan====================================\nM976 S2 P1\nG90\nG1 X128 Y128 F20000\nM976 S3 P2 ;register void printing detection\n{endif}\n\n;===== noozle load line ===============================\nM975 S1\nG90\nM83\nT1000\nG1 X18.0 Y1.0 Z0.8 F18000;Move to start position\nM109 S{nozzle_temperature[initial_no_support_extruder]}\nG1 Z0.2\nG0 E2 F300\nG0 X240 E25 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\nG0 Y15 E1.166 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\nG0 X239.5\nG0 E0.2\nG0 Y1.5 E1.166\nG0 X18 E25 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\nM400\n\n;===== for Textured PEI Plate , lower the nozzle as the nozzle was touching topmost of the texture when homing ==\n;curr_bed_type={curr_bed_type}\n{if curr_bed_type==\"Textured PEI Plate\"}\nG29.1 Z{-0.04} ; for Textured PEI Plate\n{endif}\n\n;===== draw extrinsic para cali paint =================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M1002 gcode_claim_action : 8\n\n T1000\n G0 F3000 X28.000 Y19.500 Z0.200\n G1 F1200.0 X28.000 Y45.000 Z0.200 E1.679\n G1 F1200.0 X28.500 Y45.000 Z0.200 E0.032\n G1 F1200.0 X28.500 Y19.500 Z0.200 E1.679\n G1 F1200.0 X31.000 Y19.500 Z0.200 E0.164\n G1 F1200.0 X31.000 Y49.000 Z0.200 E1.944\n G1 F1200.0 X37.500 Y49.000 Z0.200 E0.428\n G1 F1200.0 X37.500 Y60.000 Z0.200 E0.725\n G1 F1200.0 X42.500 Y60.000 Z0.200 E0.329\n G1 F1200.0 X42.500 Y49.000 Z0.200 E0.725\n G1 F1200.0 X48.000 Y49.000 Z0.200 E0.362\n G1 F1200.0 X48.000 Y20.000 Z0.200 E1.910\n G1 F1200.0 X30.000 Y20.000 Z0.200 E1.186\n G1 F1200.0 X30.000 Y41.000 Z0.200 E1.384\n G1 F1200.0 X50.000 Y41.000 Z0.200 E1.318\n G1 F1200.0 X50.000 Y34.000 Z0.200 E0.461\n G1 F1200.0 X30.000 Y34.000 Z0.200 E1.318\n G1 F1500.000 E-0.800\n\n ;=========== extruder cali extrusion ==================\n T1000\n M83\n {if default_acceleration > 0}\n {if outer_wall_acceleration > 0}\n M204 S[outer_wall_acceleration]\n {else}\n M204 S[default_acceleration]\n {endif}\n {endif}\n G0 X35.000 Y18.000 Z0.300 F30000 E0\n G1 F1500.000 E0.800\n M106 S0 ; turn off fan\n G0 X185.000 E16.9 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G0 X187 Z0\n G1 F1500.000 E-0.800\n G0 Z1\n G0 X180 Z0.3 F18000\n\n M900 L1000.0 M1.0\n M900 K0.030\n G0 X45.000 F30000\n G0 Y20.000 F30000\n G1 F1500.000 E0.800\n G1 X65.000 E2.25000 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X70.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X75.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X80.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X85.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X90.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X95.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X100.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X105.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X110.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X115.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X120.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X125.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X130.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X135.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X140.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X145.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X150.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X155.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X160.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X165.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X170.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X175.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X180.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 F1500.000 E-0.800\n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos\n G1 Z0.3\n M400\n\n G0 X45.000 F30000\n M900 K0.015\n G0 X45.000 F30000\n G0 Y22.000 F30000\n G1 F1500.000 E0.800\n G1 X65.000 E2.25000 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X70.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X75.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X80.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X85.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X90.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X95.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X100.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X105.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X110.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X115.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X120.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X125.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X130.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X135.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X140.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X145.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X150.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X155.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X160.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X165.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X170.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X175.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X180.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 F1500.000 E-0.800\n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos\n G1 Z0.3\n M400\n\n G0 X45.000 F30000\n M900 K0.000\n G0 X45.000 F30000\n G0 Y24.000 F30000\n G1 F1500.000 E0.800\n G1 X65.000 E2.25000 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X70.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X75.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X80.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X85.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X90.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X95.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X100.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X105.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X110.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X115.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X120.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X125.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X130.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X135.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X140.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X145.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X150.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X155.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X160.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X165.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X170.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X175.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X180.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 F1500.000 E-0.800\n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos\n G1 Z0.3\n\n G0 X45.000 F30000 ; move to start point\n\nM623 ; end of \"draw extrinsic para cali paint\"\n\nM104 S140\n\n\n;=========== laser and rgb calibration ===========\nM400\nM18 E\nM500 R\n\nM973 S3 P14\n\nG1 X120 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\nT1100\nG1 X235.0 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\nM400 P100\nM960 S1 P1\nM400 P100\nM973 S6 P0; use auto exposure for horizontal laser by xcam\nM960 S0 P0\n\nG1 X240.0 Y6.0 Z0.3 F18000.0;Move to vertical extrude line pos\nM960 S2 P1\nM400 P100\nM973 S6 P1; use auto exposure for vertical laser by xcam\nM960 S0 P0\n\n;=========== handeye calibration ======================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M973 S3 P1 ; camera start stream\n M400 P500\n M973 S1\n G0 F6000 X40.000 Y54.500 Z0.000\n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500\n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1\n G0 Y37.50\n M400 P200\n M971 S5 P1\n M960 S0 P0\n M960 S2 P1\n G0 Y54.50\n M400 P200\n M971 S5 P3\n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1\n G0 Y37.50\n M400 P200\n M971 S5 P2\n M960 S0 P0\n M960 S2 P1\n G0 Y54.50\n M400 P500\n M971 S5 P4\n M963 S1\n M400 P1500\n M964\n T1100\n G0 F6000 X40.000 Y54.500 Z0.000\n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500\n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1\n G0 Y37.50\n M400 P200\n M971 S5 P1\n M960 S0 P0\n M960 S2 P1\n G0 Y54.50\n M400 P200\n M971 S5 P3\n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1\n G0 Y37.50\n M400 P200\n M971 S5 P2\n M960 S0 P0\n M960 S2 P1\n G0 Y54.50\n M400 P500\n M971 S5 P4\n M963 S1\n M400 P1500\n M964\n T1100\n G1 Z3 F3000\n\n M400\n M500 ; save cali data\n\n M104 S{nozzle_temperature[initial_no_support_extruder]} ; rise nozzle temp now ,to reduce temp waiting time.\n\n T1100\n M400 P400\n M960 S0 P0\n G0 F30000.000 Y22.000 X65.000 Z0.000\n M400 P400\n M960 S1 P1\n M400 P50\n\n M969 S1 N3 A2000\n G0 F360.000 X181.000 Z0.000\n M980.3 A70.000 B{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60/4} C5.000 D{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60} E5.000 F175.000 H1.000 I0.000 J0.015 K0.030\n M400 P100\n G0 F20000\n G0 Z1 ; rise nozzle up\n T1000 ; change to nozzle space\n G0 X45.000 Y16.000 F30000 ; move to test line pos\n M969 S0 ; turn off scanning\n M960 S0 P0\n\n\n G1 Z2 F20000\n T1000\n G0 X45.000 Y16.000 F30000 E0\n M109 S{nozzle_temperature[initial_no_support_extruder]}\n G0 Z0.3\n G1 F1500.000 E3.600\n G1 X65.000 E2.25000 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X75.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X80.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X85.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X90.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X95.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X100.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X105.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X110.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X115.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X120.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X125.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X130.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X135.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n\n ; see if extrude cali success, if not ,use default value\n M1002 judge_last_extrude_cali_success\n M622 J0\n M400\n M900 K0.015 M{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*0.015}\n M623\n\n G1 X140.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X145.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X150.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X155.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X160.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X165.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X170.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X175.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X180.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X185.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X190.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X195.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X200.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X205.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X210.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X215.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X220.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X225.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n M973 S4\n\nM623\n\n;========turn off light and wait extrude temperature =============\nM1002 gcode_claim_action : 0\nM973 S4 ; turn off scanner\nM400 ; wait all motion done before implement the emprical L parameters\n;M900 L500.0 ; Empirical parameters\nM109 S[nozzle_temperature_initial_layer]\nM960 S1 P0 ; turn off laser\nM960 S2 P0 ; turn off laser\nM106 S0 ; turn off fan\nM106 P2 S0 ; turn off big fan\nM106 P3 S0 ; turn off chamber fan\n\nM975 S1 ; turn on mech mode supression\nG90\nM83\nT1000\nG1 E{-retraction_length[initial_no_support_extruder]} F1800\nG1 X128.0 Y253.0 Z0.2 F24000.0;Move to start position\nG1 E{retraction_length[initial_no_support_extruder]} F1800\nM109 S{nozzle_temperature_initial_layer[initial_no_support_extruder]}\nG0 X253 E6.4 F{outer_wall_volumetric_speed/(0.3*0.6) * 60}\nG0 Y128 E6.4\nG0 X252.5\nG0 Y252.5 E6.4\nG0 X128 E6.4", "nozzle_type": "hardened_steel", "nozzle_hrc": "55", "upward_compatible_machine":[ diff --git a/resources/profiles/BBL/machine/Bambu Lab X1 0.8 nozzle.json b/resources/profiles/BBL/machine/Bambu Lab X1 0.8 nozzle.json index 44f28edecb..b11a5b9313 100644 --- a/resources/profiles/BBL/machine/Bambu Lab X1 0.8 nozzle.json +++ b/resources/profiles/BBL/machine/Bambu Lab X1 0.8 nozzle.json @@ -29,7 +29,7 @@ "retract_length_toolchange": [ "3" ], - "machine_start_gcode": ";===== machine: X1 =========================\n;===== date: 20230130 =====================\n;===== reset machine status =================\nG91\nM17 Z0.4 ; lower the z-motor current\nG0 Z12 F300 ; lower the hotbed , to prevent the nozzle is below the hotbed\nG0 Z-6;\nG90\nM17 X1.2 Y1.2 Z0.75 ; reset motor current to default\nM960 S5 P1 ; turn on logo lamp\nG90\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\nM73.2 R1.0 ;Reset left time magnitude\nM1002 set_gcode_claim_speed_level : 5\nM221 X0 Y0 Z0 ; turn off soft endstop to prevent protential logic problem\nG29.1 Z{0.0} ; clear z-trim value first\n\n;===== heatbed preheat ====================\nM1002 gcode_claim_action : 2\nM140 S[bed_temperature_initial_layer_single] ;set bed temp\nM190 S[bed_temperature_initial_layer_single] ;wait for bed temp\n\n{if scan_first_layer}\n;=========register first layer scan=====\nM977 S1 P60\n{endif}\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_tool]==\"PLA\"}\n {if (bed_temperature[current_extruder] >45)||(bed_temperature_initial_layer[current_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[current_extruder] >50)||(bed_temperature_initial_layer[current_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n;===== prepare print temperature and material ==========\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp\nG91\nG0 Z10 F1200\nG90\nG28 X\nM975 S1 ; turn on \nG1 X60 F12000\nG1 Y245\nG1 Y265 F3000\nM620 M\nM620 S[initial_tool]A ; switch material if AMS exist\n M109 S[nozzle_temperature_initial_layer]\n G1 X120 F12000\n\n G1 X20 Y50 F12000\n G1 Y-3\n T[initial_tool]\n G1 X54 F12000\n G1 Y265\n M400\nM621 S[initial_tool]A\n\nM412 S1 ; ===turn on filament runout detection===\n\nM109 S250 ;set nozzle to common flush temp\nM106 P1 S0\nG92 E0\nG1 E50 F200\nM400\nM104 S[nozzle_temperature_initial_layer]\nG92 E0\nG1 E50 F200\nM400\nM106 P1 S255\nG92 E0\nG1 E5 F300\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20} ; drop nozzle temp, make filament shink a bit\nG92 E0\nG1 E-0.5 F300\n\nG1 X70 F9000\nG1 X76 F15000\nG1 X65 F15000\nG1 X76 F15000\nG1 X65 F15000; shake to put down garbage\nG1 X80 F6000\nG1 X95 F15000\nG1 X80 F15000\nG1 X165 F15000; wipe and shake\nM400\nM106 P1 S0\n;===== prepare print temperature and material end =====\n\n\n;===== wipe nozzle ===============================\nM1002 gcode_claim_action : 14\nM975 S1\nM106 S255\nG1 X65 Y230 F18000\nG1 Y264 F6000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20}\nG1 X100 F18000 ; first wipe mouth\n\nG0 X135 Y253 F20000 ; move to exposed steel surface edge\nG28 Z P0 T300; home z with low precision,permit 300deg temperature\nG29.2 S0 ; turn off ABL\nG0 Z5 F20000\n\nG1 X60 Y265\nG92 E0\nG1 E-0.5 F300 ; retrack more\nG1 X100 F5000; second wipe mouth\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X90 F5000\nG0 X128 Y261 Z-1.5 F20000 ; move to exposed steel surface and stop the nozzle\nM104 S140 ; set temp down to heatbed acceptable\nM106 S255 ; turn on fan (G28 has turn off fan)\n\nM221 S; push soft endstop status\nM221 Z0 ;turn off Z axis endstop\nG0 Z0.5 F20000\nG0 X125 Y259.5 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 X128\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\n\nM109 S140 ; wait nozzle temp down to heatbed acceptable\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\n\nM221 R; pop softend status\nG1 Z10 F1200\nM400\nG1 Z10\nG1 F30000\nG1 X128 Y128\nG29.2 S1 ; turn on ABL\n;G28 ; home again after hard wipe mouth\nM106 S0 ; turn off fan , too noisy\n;===== wipe nozzle end ================================\n\n;===== check scanner clarity ===========================\nG1 X128 Y128 F24000\nG28 Z P0\nM972 S5 P0\nG1 X230 Y15 F24000\n;===== check scanner clarity end =======================\n\n;===== bed leveling ==================================\nM1002 judge_flag g29_before_print_flag\nM622 J1\n\n M1002 gcode_claim_action : 1\n G29 A X{first_layer_print_min[0]} Y{first_layer_print_min[1]} I{first_layer_print_size[0]} J{first_layer_print_size[1]}\n M400\n M500 ; save cali data\n\nM623\n;===== bed leveling end ================================\n\n;===== home after wipe mouth============================\nM1002 judge_flag g29_before_print_flag\nM622 J0\n\n M1002 gcode_claim_action : 13\n G28\n\nM623\n;===== home after wipe mouth end =======================\n\nM975 S1 ; turn on vibration supression\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_tool]==\"PLA\"}\n {if (bed_temperature[current_extruder] >45)||(bed_temperature_initial_layer[current_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[current_extruder] >50)||(bed_temperature_initial_layer[current_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\nM104 S{nozzle_temperature_initial_layer[initial_extruder]} ; set extrude temp earlier, to reduce wait time\n\n;===== mech mode fast check============================\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q1 A7 B30 C80 H15 K0\nM974 Q1 S2 P0\n\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q0 A7 B30 C90 Q0 H15 K0\nM974 Q0 S2 P0\n\nM975 S1\nG1 F30000\nG1 X230 Y15\nG28 X ; re-home XY \n;===== mech mode fast check============================\n\n{if scan_first_layer}\n;start heatbed scan====================================\nM976 S2 P1 \nG90\nG1 X128 Y128 F20000\nM976 S3 P2 ;register void printing detection\n{endif}\n\n;===== noozle load line ===============================\nM975 S1\nG90 \nM83\nT1000\nG1 X18.0 Y0.5 Z0.8 F18000;Move to start position\nM109 S{nozzle_temperature[initial_extruder]}\nG1 Z0.2\nG0 E2 F300\nG0 X129 E15 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \nG0 X240 E15\nG0 Y15 E1.500 F{outer_wall_volumetric_speed/(0.3*1.0)/ 4 * 60}\nG0 X239.5\nG0 E0.3\nG0 Y1.5 E1.500\nG0 X129 E15 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \nG0 X18 E15\nM400\n\n;===== for Textured PEI Plate , lower the nozzle as the nozzle was touching topmost of the texture when homing ==\n;curr_bed_type={curr_bed_type}\n{if curr_bed_type==\"Textured PEI Plate\"}\nG29.1 Z{-0.04} ; for Textured PEI Plate\n{endif}\n\n;===== draw extrinsic para cali paint =================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M1002 gcode_claim_action : 8\n\n T1000 \n G0 F3000 X28.000 Y19.500 Z0.200\n G1 F1200.0 X28.000 Y45.000 Z0.200 E1.8660 \n G1 F1200.0 X28.500 Y45.000 Z0.200 E0.0360 \n G1 F1200.0 X28.500 Y19.500 Z0.200 E1.8660 \n G1 F1200.0 X31.000 Y19.500 Z0.200 E0.1820 \n G1 F1200.0 X31.000 Y49.000 Z0.200 E2.1600 \n G1 F1200.0 X37.500 Y49.000 Z0.200 E0.4760 \n G1 F1200.0 X37.500 Y60.000 Z0.200 E0.8060 \n G1 F1200.0 X42.500 Y60.000 Z0.200 E0.3660 \n G1 F1200.0 X42.500 Y49.000 Z0.200 E0.8060 \n G1 F1200.0 X48.000 Y49.000 Z0.200 E0.4020 \n G1 F1200.0 X48.000 Y20.000 Z0.200 E2.1220 \n G1 F1200.0 X30.000 Y20.000 Z0.200 E1.3180 \n G1 F1200.0 X30.000 Y41.000 Z0.200 E1.5380 \n G1 F1200.0 X50.000 Y41.000 Z0.200 E1.4640 \n G1 F1200.0 X50.000 Y34.000 Z0.200 E0.5120 \n G1 F1200.0 X30.000 Y34.000 Z0.200 E1.4640 \n G1 F1500.000 E-0.800 \n\n ;=========== extruder cali extrusion ================== \n T1000 \n M83\n {if default_acceleration > 0}\n {if outer_wall_acceleration > 0}\n M204 S[outer_wall_acceleration]\n {else}\n M204 S[default_acceleration]\n {endif}\n {endif}\n G0 X35.000 Y18.000 Z0.300 F30000 E0\n G1 F1500.000 E0.800 \n M106 S0 ; turn off fan\n G0 X110.000 E9.35441 F4800 \n G0 X185.000 E9.35441 F4800 \n G0 X187 Z0\n G1 F1500.000 E-0.800 \n G0 Z1\n G0 X180 Z0.3 F18000\n \n M900 L1000.0 M1.0\n M900 K0.020 \n G0 X45.000 F30000 \n G0 Y20.000 F30000 \n G1 F1500.000 E0.800 \n G1 X65.000 E2.4945 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X70.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X75.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X80.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X85.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X90.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X95.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X100.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X105.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X110.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X115.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X120.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X125.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X130.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X135.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X140.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X145.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X150.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X155.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X160.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X165.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X170.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X175.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X180.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 F1500.000 E-0.800 \n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos \n G1 Z0.3\n M400\n\n G0 X45.000 F30000 \n M900 K0.010 \n G0 X45.000 F30000 \n G0 Y22.000 F30000 \n G1 F1500.000 E0.800 \n G1 X65.000 E2.4945 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X70.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X75.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X80.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X85.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X90.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X95.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X100.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X105.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X110.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X115.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X120.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X125.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X130.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X135.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X140.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X145.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X150.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X155.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X160.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X165.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X170.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X175.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X180.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 F1500.000 E-0.800 \n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos \n G1 Z0.3\n M400\n\n G0 X45.000 F30000 \n M900 K0.000 \n G0 X45.000 F30000 \n G0 Y24.000 F30000 \n G1 F1500.000 E0.800 \n G1 X65.000 E2.4945 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X70.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X75.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X80.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X85.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X90.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X95.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X100.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X105.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X110.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X115.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X120.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X125.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X130.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X135.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X140.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X145.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X150.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X155.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X160.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X165.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X170.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X175.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X180.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 F1500.000 E-0.800\n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos \n G1 Z0.3\n\n G0 X45.000 F30000 ; move to start point\n\nM623 ; end of \"draw extrinsic para cali paint\"\n\nM104 S140\n\n\n;=========== laser and rgb calibration =========== \nM400\nM18 E\nM500 R\n\nM973 S3 P14\n\nG1 X120 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\nT1100\nG1 X143.0 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\n\nM400 P100\n\nM960 S1 P1\nM400 P100\nM973 S6 P0; use auto exposure for horizontal laser by xcam\nM960 S0 P0\n\nG1 X240.0 Y6.0 Z0.3 F18000.0;Move to vertical extrude line pos\nM960 S2 P1\nM400 P100\nM973 S6 P1; use auto exposure for vertical laser by xcam\nM960 S0 P0\n\n;=========== handeye calibration ======================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M973 S3 P1 ; camera start stream\n M400 P500\n M973 S1 \n G0 F6000 X40.000 Y54.500 Z0.000 \n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500 \n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P1 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P200 \n M971 S5 P3 \n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P2 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P500 \n M971 S5 P4 \n M963 S1 \n M400 P1500 \n M964 \n T1100 \n G0 F6000 X40.000 Y54.500 Z0.000 \n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500 \n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P1 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P200 \n M971 S5 P3 \n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P2 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P500 \n M971 S5 P4 \n M963 S1 \n M400 P1500 \n M964 \n T1100 \n G1 Z3 F3000 \n\n M400\n M500 ; save cali data\n\n M104 S{nozzle_temperature_initial_layer[initial_extruder]} ; rise nozzle temp now ,to reduce temp waiting time.\n\n T1100 \n M400 P400 \n M960 S0 P0\n G0 F30000.000 Y22.000 X65.000 Z0.000\n M400 P400 \n M960 S1 P1 \n M400 P50 \n\n M969 S1 N3 A2000 \n G0 F360.000 X181.000 Z0.000\n M980.3 A70.000 B{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60/4} C5.000 D{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60} E5.000 F175.000 H1.000 I0.000 J0.010 K0.020\n M400 P100 \n G0 F20000\n G0 Z1 ; rise nozzle up\n T1000 ; change to nozzle space\n G0 X45.000 Y16.000 F30000 ; move to test line pos\n M969 S0 ; turn off scanning\n M960 S0 P0\n\n\n G1 Z2 F20000 \n T1000 \n G0 X45.000 Y16.000 F30000 E0\n M109 S{nozzle_temperature_initial_layer[initial_extruder]} \n G0 Z0.3\n G1 F1500.000 E3.600 \n G1 X65.000 E2.4945 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X70.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X75.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X80.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X85.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X90.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X95.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X100.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X105.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X110.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X115.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X120.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X125.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X130.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X135.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n\n ; see if extrude cali success, if not ,use default value\n M1002 judge_last_extrude_cali_success\n M622 J0\n M400\n M900 K0.01 M{outer_wall_volumetric_speed/(1.75*1.75/4*3.14) *0.01}\n M623 \n\n G1 X140.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X145.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X150.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X155.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X160.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X165.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X170.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X175.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X180.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X185.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X190.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X195.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X200.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X205.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X210.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X215.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X220.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X225.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n M973 S4 \n\nM623\n\n;========turn off light and wait extrude temperature =============\nM1002 gcode_claim_action : 0\nM973 S4 ; turn off scanner\nM400 ; wait all motion done before implement the emprical L parameters\n;M900 L500.0 ; Empirical parameters\nM109 S[nozzle_temperature_initial_layer]\nM960 S1 P0 ; turn off laser\nM960 S2 P0 ; turn off laser\nM106 S0 ; turn off fan\nM106 P2 S0 ; turn off big fan \nM106 P3 S0 ; turn off chamber fan\n\nM975 S1 ; turn on mech mode supression\nG90 \nM83\nT1000\nG1 E{-retraction_length[initial_extruder]} F1800\nG1 X128.0 Y253.0 Z0.2 F24000.0;Move to start position\nG1 E{retraction_length[initial_extruder]} F1800\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG0 X253 E6.4 F{outer_wall_volumetric_speed/(0.3*0.6) * 60} \nG0 Y128 E6.4\nG0 X252.5\nG0 Y252.5 E6.4\nG0 X128 E6.4", + "machine_start_gcode": ";===== machine: X1 =========================\n;===== date: 20230322 =====================\n;===== turn on the HB fan =================\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp to turn on the HB fan\n;===== reset machine status =================\nG91\nM17 Z0.4 ; lower the z-motor current\nG0 Z12 F300 ; lower the hotbed , to prevent the nozzle is below the hotbed\nG0 Z-6;\nG90\nM17 X1.2 Y1.2 Z0.75 ; reset motor current to default\nM960 S5 P1 ; turn on logo lamp\nG90\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\nM73.2 R1.0 ;Reset left time magnitude\nM1002 set_gcode_claim_speed_level : 5\nM221 X0 Y0 Z0 ; turn off soft endstop to prevent protential logic problem\nG29.1 Z{0.0} ; clear z-trim value first\n\n;===== heatbed preheat ====================\nM1002 gcode_claim_action : 2\nM140 S[bed_temperature_initial_layer_single] ;set bed temp\nM190 S[bed_temperature_initial_layer_single] ;wait for bed temp\n\n{if scan_first_layer}\n;=========register first layer scan=====\nM977 S1 P60\n{endif}\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_no_support_extruder]==\"PLA\"}\n {if (bed_temperature[initial_no_support_extruder] >45)||(bed_temperature_initial_layer[initial_no_support_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[initial_no_support_extruder] >50)||(bed_temperature_initial_layer[initial_no_support_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n;===== prepare print temperature and material ==========\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp\nG91\nG0 Z10 F1200\nG90\nG28 X\nM975 S1 ; turn on\nG1 X60 F12000\nG1 Y245\nG1 Y265 F3000\nM620 M\nM620 S[initial_no_support_extruder]A ; switch material if AMS exist\n M109 S[nozzle_temperature_initial_layer]\n G1 X120 F12000\n\n G1 X20 Y50 F12000\n G1 Y-3\n T[initial_no_support_extruder]\n G1 X54 F12000\n G1 Y265\n M400\nM621 S[initial_no_support_extruder]A\nM620.1 E F{filament_max_volumetric_speed[initial_no_support_extruder]/2.4053*60} T{nozzle_temperature_range_high[initial_no_support_extruder]}\n\n\nM412 S1 ; ===turn on filament runout detection===\n\nM109 S250 ;set nozzle to common flush temp\nM106 P1 S0\nG92 E0\nG1 E50 F200\nM400\nM104 S[nozzle_temperature_initial_layer]\nG92 E0\nG1 E50 F200\nM400\nM106 P1 S255\nG92 E0\nG1 E5 F300\nM109 S{nozzle_temperature_initial_layer[initial_no_support_extruder]-20} ; drop nozzle temp, make filament shink a bit\nG92 E0\nG1 E-0.5 F300\n\nG1 X70 F9000\nG1 X76 F15000\nG1 X65 F15000\nG1 X76 F15000\nG1 X65 F15000; shake to put down garbage\nG1 X80 F6000\nG1 X95 F15000\nG1 X80 F15000\nG1 X165 F15000; wipe and shake\nM400\nM106 P1 S0\n;===== prepare print temperature and material end =====\n\n\n;===== wipe nozzle ===============================\nM1002 gcode_claim_action : 14\nM975 S1\nM106 S255\nG1 X65 Y230 F18000\nG1 Y264 F6000\nM109 S{nozzle_temperature_initial_layer[initial_no_support_extruder]-20}\nG1 X100 F18000 ; first wipe mouth\n\nG0 X135 Y253 F20000 ; move to exposed steel surface edge\nG28 Z P0 T300; home z with low precision,permit 300deg temperature\nG29.2 S0 ; turn off ABL\nG0 Z5 F20000\n\nG1 X60 Y265\nG92 E0\nG1 E-0.5 F300 ; retrack more\nG1 X100 F5000; second wipe mouth\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X90 F5000\nG0 X128 Y261 Z-1.5 F20000 ; move to exposed steel surface and stop the nozzle\nM104 S140 ; set temp down to heatbed acceptable\nM106 S255 ; turn on fan (G28 has turn off fan)\n\nM221 S; push soft endstop status\nM221 Z0 ;turn off Z axis endstop\nG0 Z0.5 F20000\nG0 X125 Y259.5 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 X128\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\n\nM109 S140 ; wait nozzle temp down to heatbed acceptable\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\n\nM221 R; pop softend status\nG1 Z10 F1200\nM400\nG1 Z10\nG1 F30000\nG1 X128 Y128\nG29.2 S1 ; turn on ABL\n;G28 ; home again after hard wipe mouth\nM106 S0 ; turn off fan , too noisy\n;===== wipe nozzle end ================================\n\n;===== check scanner clarity ===========================\nG1 X128 Y128 F24000\nG28 Z P0\nM972 S5 P0\nG1 X230 Y15 F24000\n;===== check scanner clarity end =======================\n\n;===== bed leveling ==================================\nM1002 judge_flag g29_before_print_flag\nM622 J1\n\n M1002 gcode_claim_action : 1\n G29 A X{first_layer_print_min[0]} Y{first_layer_print_min[1]} I{first_layer_print_size[0]} J{first_layer_print_size[1]}\n M400\n M500 ; save cali data\n\nM623\n;===== bed leveling end ================================\n\n;===== home after wipe mouth============================\nM1002 judge_flag g29_before_print_flag\nM622 J0\n\n M1002 gcode_claim_action : 13\n G28\n\nM623\n;===== home after wipe mouth end =======================\n\nM975 S1 ; turn on vibration supression\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_no_support_extruder]==\"PLA\"}\n {if (bed_temperature[initial_no_support_extruder] >45)||(bed_temperature_initial_layer[initial_no_support_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[initial_no_support_extruder] >50)||(bed_temperature_initial_layer[initial_no_support_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\nM104 S{nozzle_temperature_initial_layer[initial_no_support_extruder]} ; set extrude temp earlier, to reduce wait time\n\n;===== mech mode fast check============================\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q1 A7 B30 C80 H15 K0\nM974 Q1 S2 P0\n\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q0 A7 B30 C90 Q0 H15 K0\nM974 Q0 S2 P0\n\nM975 S1\nG1 F30000\nG1 X230 Y15\nG28 X ; re-home XY\n;===== mech mode fast check============================\n\n{if scan_first_layer}\n;start heatbed scan====================================\nM976 S2 P1\nG90\nG1 X128 Y128 F20000\nM976 S3 P2 ;register void printing detection\n{endif}\n\n;===== noozle load line ===============================\nM975 S1\nG90\nM83\nT1000\nG1 X18.0 Y0.5 Z0.8 F18000;Move to start position\nM109 S{nozzle_temperature[initial_no_support_extruder]}\nG1 Z0.2\nG0 E2 F300\nG0 X129 E15 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\nG0 X240 E15\nG0 Y15 E1.500 F{outer_wall_volumetric_speed/(0.3*1.0)/ 4 * 60}\nG0 X239.5\nG0 E0.3\nG0 Y1.5 E1.500\nG0 X129 E15 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\nG0 X18 E15\nM400\n\n;===== for Textured PEI Plate , lower the nozzle as the nozzle was touching topmost of the texture when homing ==\n;curr_bed_type={curr_bed_type}\n{if curr_bed_type==\"Textured PEI Plate\"}\nG29.1 Z{-0.04} ; for Textured PEI Plate\n{endif}\n\n;===== draw extrinsic para cali paint =================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M1002 gcode_claim_action : 8\n\n T1000\n G0 F3000 X28.000 Y19.500 Z0.200\n G1 F1200.0 X28.000 Y45.000 Z0.200 E1.8660\n G1 F1200.0 X28.500 Y45.000 Z0.200 E0.0360\n G1 F1200.0 X28.500 Y19.500 Z0.200 E1.8660\n G1 F1200.0 X31.000 Y19.500 Z0.200 E0.1820\n G1 F1200.0 X31.000 Y49.000 Z0.200 E2.1600\n G1 F1200.0 X37.500 Y49.000 Z0.200 E0.4760\n G1 F1200.0 X37.500 Y60.000 Z0.200 E0.8060\n G1 F1200.0 X42.500 Y60.000 Z0.200 E0.3660\n G1 F1200.0 X42.500 Y49.000 Z0.200 E0.8060\n G1 F1200.0 X48.000 Y49.000 Z0.200 E0.4020\n G1 F1200.0 X48.000 Y20.000 Z0.200 E2.1220\n G1 F1200.0 X30.000 Y20.000 Z0.200 E1.3180\n G1 F1200.0 X30.000 Y41.000 Z0.200 E1.5380\n G1 F1200.0 X50.000 Y41.000 Z0.200 E1.4640\n G1 F1200.0 X50.000 Y34.000 Z0.200 E0.5120\n G1 F1200.0 X30.000 Y34.000 Z0.200 E1.4640\n G1 F1500.000 E-0.800\n\n ;=========== extruder cali extrusion ==================\n T1000\n M83\n {if default_acceleration > 0}\n {if outer_wall_acceleration > 0}\n M204 S[outer_wall_acceleration]\n {else}\n M204 S[default_acceleration]\n {endif}\n {endif}\n G0 X35.000 Y18.000 Z0.300 F30000 E0\n G1 F1500.000 E0.800\n M106 S0 ; turn off fan\n G0 X110.000 E9.35441 F4800\n G0 X185.000 E9.35441 F4800\n G0 X187 Z0\n G1 F1500.000 E-0.800\n G0 Z1\n G0 X180 Z0.3 F18000\n\n M900 L1000.0 M1.0\n M900 K0.020\n G0 X45.000 F30000\n G0 Y20.000 F30000\n G1 F1500.000 E0.800\n G1 X65.000 E2.4945 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X70.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X75.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X80.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X85.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X90.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X95.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X100.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X105.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X110.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X115.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X120.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X125.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X130.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X135.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X140.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X145.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X150.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X155.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X160.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X165.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X170.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X175.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X180.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 F1500.000 E-0.800\n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos\n G1 Z0.3\n M400\n\n G0 X45.000 F30000\n M900 K0.010\n G0 X45.000 F30000\n G0 Y22.000 F30000\n G1 F1500.000 E0.800\n G1 X65.000 E2.4945 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X70.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X75.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X80.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X85.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X90.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X95.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X100.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X105.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X110.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X115.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X120.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X125.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X130.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X135.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X140.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X145.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X150.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X155.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X160.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X165.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X170.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X175.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X180.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 F1500.000 E-0.800\n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos\n G1 Z0.3\n M400\n\n G0 X45.000 F30000\n M900 K0.000\n G0 X45.000 F30000\n G0 Y24.000 F30000\n G1 F1500.000 E0.800\n G1 X65.000 E2.4945 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X70.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X75.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X80.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X85.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X90.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X95.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X100.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X105.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X110.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X115.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X120.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X125.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X130.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X135.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X140.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X145.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X150.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X155.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X160.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X165.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X170.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X175.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X180.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 F1500.000 E-0.800\n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos\n G1 Z0.3\n\n G0 X45.000 F30000 ; move to start point\n\nM623 ; end of \"draw extrinsic para cali paint\"\n\nM104 S140\n\n\n;=========== laser and rgb calibration ===========\nM400\nM18 E\nM500 R\n\nM973 S3 P14\n\nG1 X120 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\nT1100\nG1 X235.0 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\nM400 P100\nM960 S1 P1\nM400 P100\nM973 S6 P0; use auto exposure for horizontal laser by xcam\nM960 S0 P0\n\nG1 X240.0 Y6.0 Z0.3 F18000.0;Move to vertical extrude line pos\nM960 S2 P1\nM400 P100\nM973 S6 P1; use auto exposure for vertical laser by xcam\nM960 S0 P0\n\n;=========== handeye calibration ======================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M973 S3 P1 ; camera start stream\n M400 P500\n M973 S1\n G0 F6000 X40.000 Y54.500 Z0.000\n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500\n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1\n G0 Y37.50\n M400 P200\n M971 S5 P1\n M960 S0 P0\n M960 S2 P1\n G0 Y54.50\n M400 P200\n M971 S5 P3\n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1\n G0 Y37.50\n M400 P200\n M971 S5 P2\n M960 S0 P0\n M960 S2 P1\n G0 Y54.50\n M400 P500\n M971 S5 P4\n M963 S1\n M400 P1500\n M964\n T1100\n G0 F6000 X40.000 Y54.500 Z0.000\n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500\n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1\n G0 Y37.50\n M400 P200\n M971 S5 P1\n M960 S0 P0\n M960 S2 P1\n G0 Y54.50\n M400 P200\n M971 S5 P3\n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1\n G0 Y37.50\n M400 P200\n M971 S5 P2\n M960 S0 P0\n M960 S2 P1\n G0 Y54.50\n M400 P500\n M971 S5 P4\n M963 S1\n M400 P1500\n M964\n T1100\n G1 Z3 F3000\n\n M400\n M500 ; save cali data\n\n M104 S{nozzle_temperature_initial_layer[initial_no_support_extruder]} ; rise nozzle temp now ,to reduce temp waiting time.\n\n T1100\n M400 P400\n M960 S0 P0\n G0 F30000.000 Y22.000 X65.000 Z0.000\n M400 P400\n M960 S1 P1\n M400 P50\n\n M969 S1 N3 A2000\n G0 F360.000 X181.000 Z0.000\n M980.3 A70.000 B{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60/4} C5.000 D{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60} E5.000 F175.000 H1.000 I0.000 J0.010 K0.020\n M400 P100\n G0 F20000\n G0 Z1 ; rise nozzle up\n T1000 ; change to nozzle space\n G0 X45.000 Y16.000 F30000 ; move to test line pos\n M969 S0 ; turn off scanning\n M960 S0 P0\n\n\n G1 Z2 F20000\n T1000\n G0 X45.000 Y16.000 F30000 E0\n M109 S{nozzle_temperature_initial_layer[initial_no_support_extruder]}\n G0 Z0.3\n G1 F1500.000 E3.600\n G1 X65.000 E2.4945 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X70.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X75.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X80.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X85.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X90.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X95.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X100.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X105.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X110.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X115.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X120.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X125.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X130.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X135.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n\n ; see if extrude cali success, if not ,use default value\n M1002 judge_last_extrude_cali_success\n M622 J0\n M400\n M900 K0.01 M{outer_wall_volumetric_speed/(1.75*1.75/4*3.14) *0.01}\n M623\n\n G1 X140.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X145.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X150.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X155.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X160.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X165.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X170.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X175.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X180.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X185.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X190.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X195.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X200.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X205.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X210.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X215.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X220.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X225.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n M973 S4\n\nM623\n\n;========turn off light and wait extrude temperature =============\nM1002 gcode_claim_action : 0\nM973 S4 ; turn off scanner\nM400 ; wait all motion done before implement the emprical L parameters\n;M900 L500.0 ; Empirical parameters\nM109 S[nozzle_temperature_initial_layer]\nM960 S1 P0 ; turn off laser\nM960 S2 P0 ; turn off laser\nM106 S0 ; turn off fan\nM106 P2 S0 ; turn off big fan\nM106 P3 S0 ; turn off chamber fan\n\nM975 S1 ; turn on mech mode supression\nG90\nM83\nT1000\nG1 E{-retraction_length[initial_no_support_extruder]} F1800\nG1 X128.0 Y253.0 Z0.2 F24000.0;Move to start position\nG1 E{retraction_length[initial_no_support_extruder]} F1800\nM109 S{nozzle_temperature_initial_layer[initial_no_support_extruder]}\nG0 X253 E6.4 F{outer_wall_volumetric_speed/(0.3*0.6) * 60}\nG0 Y128 E6.4\nG0 X252.5\nG0 Y252.5 E6.4\nG0 X128 E6.4", "nozzle_type": "hardened_steel", "nozzle_hrc": "55", "upward_compatible_machine":[ diff --git a/resources/profiles/BBL/machine/Bambu Lab X1 Carbon 0.2 nozzle.json b/resources/profiles/BBL/machine/Bambu Lab X1 Carbon 0.2 nozzle.json index fba24a6a05..fd99ccab49 100644 --- a/resources/profiles/BBL/machine/Bambu Lab X1 Carbon 0.2 nozzle.json +++ b/resources/profiles/BBL/machine/Bambu Lab X1 Carbon 0.2 nozzle.json @@ -26,7 +26,7 @@ "retraction_minimum_travel": [ "5" ], - "machine_start_gcode": ";===== machine: X1 =========================\n;===== date: 20230130 =====================\n;===== reset machine status =================\nG91\nM17 Z0.4 ; lower the z-motor current\nG0 Z12 F300 ; lower the hotbed , to prevent the nozzle is below the hotbed\nG0 Z-6;\nG90\nM17 X1.2 Y1.2 Z0.75 ; reset motor current to default\nM960 S5 P1 ; turn on logo lamp\nG90\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\nM73.2 R1.0 ;Reset left time magnitude\nM1002 set_gcode_claim_speed_level : 5\nM221 X0 Y0 Z0 ; turn off soft endstop to prevent protential logic problem\nG29.1 Z{+0.0} ; clear z-trim value first\n\n;===== heatbed preheat ====================\nM1002 gcode_claim_action : 2\nM140 S[bed_temperature_initial_layer_single] ;set bed temp\nM190 S[bed_temperature_initial_layer_single] ;wait for bed temp\n\n{if scan_first_layer}\n;=========register first layer scan=====\nM977 S1 P60\n{endif}\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_tool]==\"PLA\"}\n {if (bed_temperature[current_extruder] >45)||(bed_temperature_initial_layer[current_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[current_extruder] >50)||(bed_temperature_initial_layer[current_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n;===== prepare print temperature and material ==========\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp\nG91\nG0 Z10 F1200\nG90\nG28 X\nM975 S1 ; turn on \nG1 X60 F12000\nG1 Y245\nG1 Y265 F3000\nM620 M\nM620 S[initial_tool]A ; switch material if AMS exist\n M109 S[nozzle_temperature_initial_layer]\n G1 X120 F12000\n\n G1 X20 Y50 F12000\n G1 Y-3\n T[initial_tool]\n G1 X54 F12000\n G1 Y265\n M400\nM621 S[initial_tool]A\n\nM412 S1 ; ===turn on filament runout detection===\n\nM109 S250 ;set nozzle to common flush temp\nM106 P1 S0\nG92 E0\nG1 E50 F200\nM400\nM104 S[nozzle_temperature_initial_layer]\nG92 E0\nG1 E50 F200\nM400\nM106 P1 S255\nG92 E0\nG1 E5 F300\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20} ; drop nozzle temp, make filament shink a bit\nG92 E0\nG1 E-0.5 F300\n\nG1 X70 F9000\nG1 X76 F15000\nG1 X65 F15000\nG1 X76 F15000\nG1 X65 F15000; shake to put down garbage\nG1 X80 F6000\nG1 X95 F15000\nG1 X80 F15000\nG1 X165 F15000; wipe and shake\nM400\nM106 P1 S0\n;===== prepare print temperature and material end =====\n\n\n;===== wipe nozzle ===============================\nM1002 gcode_claim_action : 14\nM975 S1\nM106 S255\nG1 X65 Y230 F18000\nG1 Y264 F6000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20}\nG1 X100 F18000 ; first wipe mouth\n\nG0 X135 Y253 F20000 ; move to exposed steel surface edge\nG28 Z P0 T300; home z with low precision,permit 300deg temperature\nG29.2 S0 ; turn off ABL\nG0 Z5 F20000\n\nG1 X60 Y265\nG92 E0\nG1 E-0.5 F300 ; retrack more\nG1 X100 F5000; second wipe mouth\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X90 F5000\nG0 X128 Y261 Z-1.5 F20000 ; move to exposed steel surface and stop the nozzle\nM104 S140 ; set temp down to heatbed acceptable\nM106 S255 ; turn on fan (G28 has turn off fan)\n\nM221 S; push soft endstop status\nM221 Z0 ;turn off Z axis endstop\nG0 Z0.5 F20000\nG0 X125 Y259.5 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 X128\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\n\nM109 S140 ; wait nozzle temp down to heatbed acceptable\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\n\nM221 R; pop softend status\nG1 Z10 F1200\nM400\nG1 Z10\nG1 F30000\nG1 X128 Y128\nG29.2 S1 ; turn on ABL\n;G28 ; home again after hard wipe mouth\nM106 S0 ; turn off fan , too noisy\n;===== wipe nozzle end ================================\n\n;===== check scanner clarity ===========================\nG1 X128 Y128 F24000\nG28 Z P0\nM972 S5 P0\nG1 X230 Y15 F24000\n;===== check scanner clarity end =======================\n\n;===== bed leveling ==================================\nM1002 judge_flag g29_before_print_flag\nM622 J1\n\n M1002 gcode_claim_action : 1\n G29 A X{first_layer_print_min[0]} Y{first_layer_print_min[1]} I{first_layer_print_size[0]} J{first_layer_print_size[1]}\n M400\n M500 ; save cali data\n\nM623\n;===== bed leveling end ================================\n\n;===== home after wipe mouth============================\nM1002 judge_flag g29_before_print_flag\nM622 J0\n\n M1002 gcode_claim_action : 13\n G28\n\nM623\n;===== home after wipe mouth end =======================\n\nM975 S1 ; turn on vibration supression\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_tool]==\"PLA\"}\n {if (bed_temperature[current_extruder] >45)||(bed_temperature_initial_layer[current_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[current_extruder] >50)||(bed_temperature_initial_layer[current_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\nM104 S{nozzle_temperature_initial_layer[initial_extruder]} ; set extrude temp earlier, to reduce wait time\n\n;===== mech mode fast check============================\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q1 A7 B30 C80 H15 K0\nM974 Q1 S2 P0\n\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q0 A7 B30 C90 Q0 H15 K0\nM974 Q0 S2 P0\n\nM975 S1\nG1 F30000\nG1 X230 Y15\nG28 X ; re-home XY \n;===== mech mode fast check============================\n\n{if scan_first_layer}\n;start heatbed scan====================================\nM976 S2 P1 \nG90\nG1 X128 Y128 F20000\nM976 S3 P2 ;register void printing detection\n{endif}\n\n;===== noozle load line ===============================\nM975 S1\nG90 \nM83\nT1000\nG1 X18.0 Y1.0 Z0.8 F18000;Move to start position\nM109 S{nozzle_temperature[initial_extruder]}\nG1 Z0.2\nG0 E2 F300\nG0 X240 E15 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \nG0 Y11 E0.700 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\nG0 X239.5\nG0 E0.2\nG0 Y1.5 E0.700\nG0 X18 E15 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \nM400\n\n;===== for Textured PEI Plate , lower the nozzle as the nozzle was touching topmost of the texture when homing ==\n;curr_bed_type={curr_bed_type}\n{if curr_bed_type==\"Textured PEI Plate\"}\nG29.1 Z{-0.04} ; for Textured PEI Plate\n{endif}\n\n;===== draw extrinsic para cali paint =================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M1002 gcode_claim_action : 8\n\n T1000 \n G0 F3000 X28.000 Y19.500 Z0.200\n G1 F1200.0 X28.000 Y45.000 Z0.200 E0.933 \n G1 F1200.0 X28.500 Y45.000 Z0.200 E0.018 \n G1 F1200.0 X28.500 Y19.500 Z0.200 E0.933 \n G1 F1200.0 X31.000 Y19.500 Z0.200 E0.091 \n G1 F1200.0 X31.000 Y49.000 Z0.200 E1.080 \n G1 F1200.0 X37.500 Y49.000 Z0.200 E0.238 \n G1 F1200.0 X37.500 Y60.000 Z0.200 E0.403 \n G1 F1200.0 X42.500 Y60.000 Z0.200 E0.183 \n G1 F1200.0 X42.500 Y49.000 Z0.200 E0.403 \n G1 F1200.0 X48.000 Y49.000 Z0.200 E0.201 \n G1 F1200.0 X48.000 Y20.000 Z0.200 E1.061 \n G1 F1200.0 X30.000 Y20.000 Z0.200 E0.659 \n G1 F1200.0 X30.000 Y41.000 Z0.200 E0.769 \n G1 F1200.0 X50.000 Y41.000 Z0.200 E0.732 \n G1 F1200.0 X50.000 Y34.000 Z0.200 E0.256 \n G1 F1200.0 X30.000 Y34.000 Z0.200 E0.732 \n G1 F1500.000 E-0.800 \n\n ;=========== extruder cali extrusion ================== \n T1000 \n M83\n\t{if default_acceleration > 0}\n {if outer_wall_acceleration > 0}\n M204 S[outer_wall_acceleration]\n {else}\n M204 S[default_acceleration]\n {endif}\n {endif}\n G0 X35.000 Y18.000 Z0.300 F30000 E0\n G1 F1500.000 E0.800 \n M106 S0 ; turn off fan\n G0 X185.000 E9.35441 F4800 \n G0 X187 Z0\n G1 F1500.000 E-0.800 \n G0 Z1\n G0 X180 Z0.3 F18000\n \n M900 L1000.0 M1.0\n M900 K0.160 \n G0 X45.000 F30000 \n G0 Y20.000 F30000 \n G1 F1500.000 E0.800 \n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 F1500.000 E-0.800 \n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos \n G1 Z0.3\n M400\n\n G0 X45.000 F30000 \n M900 K0.080 \n G0 X45.000 F30000 \n G0 Y22.000 F30000 \n G1 F1500.000 E0.800 \n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 F1500.000 E-0.800 \n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos \n G1 Z0.3\n M400\n\n G0 X45.000 F30000 \n M900 K0.000 \n G0 X45.000 F30000 \n G0 Y24.000 F30000 \n G1 F1500.000 E0.800 \n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 F1500.000 E-0.800\n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos \n G1 Z0.3\n\n G0 X45.000 F30000 ; move to start point\n\nM623 ; end of \"draw extrinsic para cali paint\"\n\nM104 S140\n\n\n;=========== laser and rgb calibration =========== \nM400\nM18 E\nM500 R\n\nM973 S3 P14\n\nG1 X120 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\nT1100\nG1 X143.0 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\n\nM400 P100\n\nM960 S1 P1\nM400 P100\nM973 S6 P0; use auto exposure for horizontal laser by xcam\nM960 S0 P0\n\nG1 X240.0 Y6.0 Z0.3 F18000.0;Move to vertical extrude line pos\nM960 S2 P1\nM400 P100\nM973 S6 P1; use auto exposure for vertical laser by xcam\nM960 S0 P0\n\n;=========== handeye calibration ======================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M973 S3 P1 ; camera start stream\n M400 P500\n M973 S1 \n G0 F6000 X40.000 Y54.500 Z0.000 \n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500 \n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P1 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P200 \n M971 S5 P3 \n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P2 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P500 \n M971 S5 P4 \n M963 S1 \n M400 P1500 \n M964 \n T1100 \n G0 F6000 X40.000 Y54.500 Z0.000 \n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500 \n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P1 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P200 \n M971 S5 P3 \n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P2 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P500 \n M971 S5 P4 \n M963 S1 \n M400 P1500 \n M964 \n T1100 \n G1 Z3 F3000 \n\n M400\n M500 ; save cali data\n\n M104 S{nozzle_temperature[initial_extruder]} ; rise nozzle temp now ,to reduce temp waiting time.\n\n T1100 \n M400 P400 \n M960 S0 P0\n G0 F30000.000 Y22.000 X65.000 Z0.000\n M400 P400 \n M960 S1 P1 \n M400 P50 \n\n M969 S1 N3 A2000 \n G0 F360.000 X181.000 Z0.000\n M980.3 A70.000 B{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60/4} C5.000 D{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60} E5.000 F175.000 H1.000 I0.000 J0.080 K0.160\n M400 P100 \n G0 F20000\n G0 Z1 ; rise nozzle up\n T1000 ; change to nozzle space\n G0 X45.000 Y16.000 F30000 ; move to test line pos\n M969 S0 ; turn off scanning\n M960 S0 P0\n\n\n G1 Z2 F20000 \n T1000 \n G0 X45.000 Y16.000 F30000 E0\n M109 S{nozzle_temperature[initial_extruder]}\n G0 Z0.3\n G1 F1500.000 E3.600 \n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n\n ; see if extrude cali success, if not ,use default value\n M1002 judge_last_extrude_cali_success\n M622 J0\n M400\n M900 K0.08 M{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*0.08}\n M623 \n\n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X185.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X190.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X195.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X200.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X205.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X210.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X215.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X220.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X225.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n M973 S4 \n\nM623\n\n;========turn off light and wait extrude temperature =============\nM1002 gcode_claim_action : 0\nM973 S4 ; turn off scanner\nM400 ; wait all motion done before implement the emprical L parameters\n;M900 L500.0 ; Empirical parameters\nM109 S[nozzle_temperature_initial_layer]\nM960 S1 P0 ; turn off laser\nM960 S2 P0 ; turn off laser\nM106 S0 ; turn off fan\nM106 P2 S0 ; turn off big fan \nM106 P3 S0 ; turn off chamber fan\n\nM975 S1 ; turn on mech mode supression\nG90 \nM83\nT1000\nG1 E{-retraction_length[initial_extruder]} F1800\nG1 X128.0 Y253.0 Z0.2 F24000.0;Move to start position\nG1 E{retraction_length[initial_extruder]} F1800\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG0 X253 E6.4 F{outer_wall_volumetric_speed/(0.3*0.6) * 60} \nG0 Y128 E6.4\nG0 X252.5\nG0 Y252.5 E6.4\nG0 X128 E6.4", + "machine_start_gcode": ";===== machine: X1 =========================\n;===== date: 20230322 =====================\n;===== turn on the HB fan =================\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp to turn on the HB fan\n;===== reset machine status =================\nG91\nM17 Z0.4 ; lower the z-motor current\nG0 Z12 F300 ; lower the hotbed , to prevent the nozzle is below the hotbed\nG0 Z-6;\nG90\nM17 X1.2 Y1.2 Z0.75 ; reset motor current to default\nM960 S5 P1 ; turn on logo lamp\nG90\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\nM73.2 R1.0 ;Reset left time magnitude\nM1002 set_gcode_claim_speed_level : 5\nM221 X0 Y0 Z0 ; turn off soft endstop to prevent protential logic problem\nG29.1 Z{+0.0} ; clear z-trim value first\n\n;===== heatbed preheat ====================\nM1002 gcode_claim_action : 2\nM140 S[bed_temperature_initial_layer_single] ;set bed temp\nM190 S[bed_temperature_initial_layer_single] ;wait for bed temp\n\n{if scan_first_layer}\n;=========register first layer scan=====\nM977 S1 P60\n{endif}\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_no_support_extruder]==\"PLA\"}\n {if (bed_temperature[initial_no_support_extruder] >45)||(bed_temperature_initial_layer[initial_no_support_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[initial_no_support_extruder] >50)||(bed_temperature_initial_layer[initial_no_support_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n;===== prepare print temperature and material ==========\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp\nG91\nG0 Z10 F1200\nG90\nG28 X\nM975 S1 ; turn on\nG1 X60 F12000\nG1 Y245\nG1 Y265 F3000\nM620 M\nM620 S[initial_no_support_extruder]A ; switch material if AMS exist\n M109 S[nozzle_temperature_initial_layer]\n G1 X120 F12000\n\n G1 X20 Y50 F12000\n G1 Y-3\n T[initial_no_support_extruder]\n G1 X54 F12000\n G1 Y265\n M400\nM621 S[initial_no_support_extruder]A\nM620.1 E F{filament_max_volumetric_speed[initial_no_support_extruder]/2.4053*60} T{nozzle_temperature_range_high[initial_no_support_extruder]}\n\n\nM412 S1 ; ===turn on filament runout detection===\n\nM109 S250 ;set nozzle to common flush temp\nM106 P1 S0\nG92 E0\nG1 E50 F200\nM400\nM104 S[nozzle_temperature_initial_layer]\nG92 E0\nG1 E50 F200\nM400\nM106 P1 S255\nG92 E0\nG1 E5 F300\nM109 S{nozzle_temperature_initial_layer[initial_no_support_extruder]-20} ; drop nozzle temp, make filament shink a bit\nG92 E0\nG1 E-0.5 F300\n\nG1 X70 F9000\nG1 X76 F15000\nG1 X65 F15000\nG1 X76 F15000\nG1 X65 F15000; shake to put down garbage\nG1 X80 F6000\nG1 X95 F15000\nG1 X80 F15000\nG1 X165 F15000; wipe and shake\nM400\nM106 P1 S0\n;===== prepare print temperature and material end =====\n\n\n;===== wipe nozzle ===============================\nM1002 gcode_claim_action : 14\nM975 S1\nM106 S255\nG1 X65 Y230 F18000\nG1 Y264 F6000\nM109 S{nozzle_temperature_initial_layer[initial_no_support_extruder]-20}\nG1 X100 F18000 ; first wipe mouth\n\nG0 X135 Y253 F20000 ; move to exposed steel surface edge\nG28 Z P0 T300; home z with low precision,permit 300deg temperature\nG29.2 S0 ; turn off ABL\nG0 Z5 F20000\n\nG1 X60 Y265\nG92 E0\nG1 E-0.5 F300 ; retrack more\nG1 X100 F5000; second wipe mouth\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X90 F5000\nG0 X128 Y261 Z-1.5 F20000 ; move to exposed steel surface and stop the nozzle\nM104 S140 ; set temp down to heatbed acceptable\nM106 S255 ; turn on fan (G28 has turn off fan)\n\nM221 S; push soft endstop status\nM221 Z0 ;turn off Z axis endstop\nG0 Z0.5 F20000\nG0 X125 Y259.5 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 X128\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\n\nM109 S140 ; wait nozzle temp down to heatbed acceptable\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\n\nM221 R; pop softend status\nG1 Z10 F1200\nM400\nG1 Z10\nG1 F30000\nG1 X128 Y128\nG29.2 S1 ; turn on ABL\n;G28 ; home again after hard wipe mouth\nM106 S0 ; turn off fan , too noisy\n;===== wipe nozzle end ================================\n\n;===== check scanner clarity ===========================\nG1 X128 Y128 F24000\nG28 Z P0\nM972 S5 P0\nG1 X230 Y15 F24000\n;===== check scanner clarity end =======================\n\n;===== bed leveling ==================================\nM1002 judge_flag g29_before_print_flag\nM622 J1\n\n M1002 gcode_claim_action : 1\n G29 A X{first_layer_print_min[0]} Y{first_layer_print_min[1]} I{first_layer_print_size[0]} J{first_layer_print_size[1]}\n M400\n M500 ; save cali data\n\nM623\n;===== bed leveling end ================================\n\n;===== home after wipe mouth============================\nM1002 judge_flag g29_before_print_flag\nM622 J0\n\n M1002 gcode_claim_action : 13\n G28\n\nM623\n;===== home after wipe mouth end =======================\n\nM975 S1 ; turn on vibration supression\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_no_support_extruder]==\"PLA\"}\n {if (bed_temperature[initial_no_support_extruder] >45)||(bed_temperature_initial_layer[initial_no_support_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[initial_no_support_extruder] >50)||(bed_temperature_initial_layer[initial_no_support_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\nM104 S{nozzle_temperature_initial_layer[initial_no_support_extruder]} ; set extrude temp earlier, to reduce wait time\n\n;===== mech mode fast check============================\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q1 A7 B30 C80 H15 K0\nM974 Q1 S2 P0\n\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q0 A7 B30 C90 Q0 H15 K0\nM974 Q0 S2 P0\n\nM975 S1\nG1 F30000\nG1 X230 Y15\nG28 X ; re-home XY\n;===== mech mode fast check============================\n\n{if scan_first_layer}\n;start heatbed scan====================================\nM976 S2 P1\nG90\nG1 X128 Y128 F20000\nM976 S3 P2 ;register void printing detection\n{endif}\n\n;===== noozle load line ===============================\nM975 S1\nG90\nM83\nT1000\nG1 X18.0 Y1.0 Z0.8 F18000;Move to start position\nM109 S{nozzle_temperature[initial_no_support_extruder]}\nG1 Z0.2\nG0 E2 F300\nG0 X240 E15 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\nG0 Y11 E0.700 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\nG0 X239.5\nG0 E0.2\nG0 Y1.5 E0.700\nG0 X18 E15 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\nM400\n\n;===== for Textured PEI Plate , lower the nozzle as the nozzle was touching topmost of the texture when homing ==\n;curr_bed_type={curr_bed_type}\n{if curr_bed_type==\"Textured PEI Plate\"}\nG29.1 Z{-0.04} ; for Textured PEI Plate\n{endif}\n\n;===== draw extrinsic para cali paint =================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M1002 gcode_claim_action : 8\n\n T1000\n G0 F3000 X28.000 Y19.500 Z0.200\n G1 F1200.0 X28.000 Y45.000 Z0.200 E0.933\n G1 F1200.0 X28.500 Y45.000 Z0.200 E0.018\n G1 F1200.0 X28.500 Y19.500 Z0.200 E0.933\n G1 F1200.0 X31.000 Y19.500 Z0.200 E0.091\n G1 F1200.0 X31.000 Y49.000 Z0.200 E1.080\n G1 F1200.0 X37.500 Y49.000 Z0.200 E0.238\n G1 F1200.0 X37.500 Y60.000 Z0.200 E0.403\n G1 F1200.0 X42.500 Y60.000 Z0.200 E0.183\n G1 F1200.0 X42.500 Y49.000 Z0.200 E0.403\n G1 F1200.0 X48.000 Y49.000 Z0.200 E0.201\n G1 F1200.0 X48.000 Y20.000 Z0.200 E1.061\n G1 F1200.0 X30.000 Y20.000 Z0.200 E0.659\n G1 F1200.0 X30.000 Y41.000 Z0.200 E0.769\n G1 F1200.0 X50.000 Y41.000 Z0.200 E0.732\n G1 F1200.0 X50.000 Y34.000 Z0.200 E0.256\n G1 F1200.0 X30.000 Y34.000 Z0.200 E0.732\n G1 F1500.000 E-0.800\n\n ;=========== extruder cali extrusion ==================\n T1000\n M83\n\t{if default_acceleration > 0}\n {if outer_wall_acceleration > 0}\n M204 S[outer_wall_acceleration]\n {else}\n M204 S[default_acceleration]\n {endif}\n {endif}\n G0 X35.000 Y18.000 Z0.300 F30000 E0\n G1 F1500.000 E0.800\n M106 S0 ; turn off fan\n G0 X185.000 E9.35441 F4800\n G0 X187 Z0\n G1 F1500.000 E-0.800\n G0 Z1\n G0 X180 Z0.3 F18000\n\n M900 L1000.0 M1.0\n M900 K0.160\n G0 X45.000 F30000\n G0 Y20.000 F30000\n G1 F1500.000 E0.800\n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 F1500.000 E-0.800\n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos\n G1 Z0.3\n M400\n\n G0 X45.000 F30000\n M900 K0.080\n G0 X45.000 F30000\n G0 Y22.000 F30000\n G1 F1500.000 E0.800\n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 F1500.000 E-0.800\n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos\n G1 Z0.3\n M400\n\n G0 X45.000 F30000\n M900 K0.000\n G0 X45.000 F30000\n G0 Y24.000 F30000\n G1 F1500.000 E0.800\n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 F1500.000 E-0.800\n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos\n G1 Z0.3\n\n G0 X45.000 F30000 ; move to start point\n\nM623 ; end of \"draw extrinsic para cali paint\"\n\nM104 S140\n\n\n;=========== laser and rgb calibration ===========\nM400\nM18 E\nM500 R\n\nM973 S3 P14\n\nG1 X120 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\nT1100\nG1 X235.0 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\nM400 P100\nM960 S1 P1\nM400 P100\nM973 S6 P0; use auto exposure for horizontal laser by xcam\nM960 S0 P0\n\nG1 X240.0 Y6.0 Z0.3 F18000.0;Move to vertical extrude line pos\nM960 S2 P1\nM400 P100\nM973 S6 P1; use auto exposure for vertical laser by xcam\nM960 S0 P0\n\n;=========== handeye calibration ======================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M973 S3 P1 ; camera start stream\n M400 P500\n M973 S1\n G0 F6000 X40.000 Y54.500 Z0.000\n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500\n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1\n G0 Y37.50\n M400 P200\n M971 S5 P1\n M960 S0 P0\n M960 S2 P1\n G0 Y54.50\n M400 P200\n M971 S5 P3\n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1\n G0 Y37.50\n M400 P200\n M971 S5 P2\n M960 S0 P0\n M960 S2 P1\n G0 Y54.50\n M400 P500\n M971 S5 P4\n M963 S1\n M400 P1500\n M964\n T1100\n G0 F6000 X40.000 Y54.500 Z0.000\n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500\n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1\n G0 Y37.50\n M400 P200\n M971 S5 P1\n M960 S0 P0\n M960 S2 P1\n G0 Y54.50\n M400 P200\n M971 S5 P3\n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1\n G0 Y37.50\n M400 P200\n M971 S5 P2\n M960 S0 P0\n M960 S2 P1\n G0 Y54.50\n M400 P500\n M971 S5 P4\n M963 S1\n M400 P1500\n M964\n T1100\n G1 Z3 F3000\n\n M400\n M500 ; save cali data\n\n M104 S{nozzle_temperature[initial_no_support_extruder]} ; rise nozzle temp now ,to reduce temp waiting time.\n\n T1100\n M400 P400\n M960 S0 P0\n G0 F30000.000 Y22.000 X65.000 Z0.000\n M400 P400\n M960 S1 P1\n M400 P50\n\n M969 S1 N3 A2000\n G0 F360.000 X181.000 Z0.000\n M980.3 A70.000 B{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60/4} C5.000 D{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60} E5.000 F175.000 H1.000 I0.000 J0.080 K0.160\n M400 P100\n G0 F20000\n G0 Z1 ; rise nozzle up\n T1000 ; change to nozzle space\n G0 X45.000 Y16.000 F30000 ; move to test line pos\n M969 S0 ; turn off scanning\n M960 S0 P0\n\n\n G1 Z2 F20000\n T1000\n G0 X45.000 Y16.000 F30000 E0\n M109 S{nozzle_temperature[initial_no_support_extruder]}\n G0 Z0.3\n G1 F1500.000 E3.600\n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n\n ; see if extrude cali success, if not ,use default value\n M1002 judge_last_extrude_cali_success\n M622 J0\n M400\n M900 K0.08 M{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*0.08}\n M623\n\n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X185.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X190.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X195.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X200.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X205.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X210.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X215.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X220.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X225.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n M973 S4\n\nM623\n\n;========turn off light and wait extrude temperature =============\nM1002 gcode_claim_action : 0\nM973 S4 ; turn off scanner\nM400 ; wait all motion done before implement the emprical L parameters\n;M900 L500.0 ; Empirical parameters\nM109 S[nozzle_temperature_initial_layer]\nM960 S1 P0 ; turn off laser\nM960 S2 P0 ; turn off laser\nM106 S0 ; turn off fan\nM106 P2 S0 ; turn off big fan\nM106 P3 S0 ; turn off chamber fan\n\nM975 S1 ; turn on mech mode supression\nG90\nM83\nT1000\nG1 E{-retraction_length[initial_no_support_extruder]} F1800\nG1 X128.0 Y253.0 Z0.2 F24000.0;Move to start position\nG1 E{retraction_length[initial_no_support_extruder]} F1800\nM109 S{nozzle_temperature_initial_layer[initial_no_support_extruder]}\nG0 X253 E6.4 F{outer_wall_volumetric_speed/(0.3*0.6) * 60}\nG0 Y128 E6.4\nG0 X252.5\nG0 Y252.5 E6.4\nG0 X128 E6.4", "nozzle_type": "stainless_steel", "nozzle_hrc": "20", "upward_compatible_machine":[ diff --git a/resources/profiles/BBL/machine/Bambu Lab X1 Carbon 0.4 nozzle.json b/resources/profiles/BBL/machine/Bambu Lab X1 Carbon 0.4 nozzle.json index 9e786ff24f..0b079aca78 100644 --- a/resources/profiles/BBL/machine/Bambu Lab X1 Carbon 0.4 nozzle.json +++ b/resources/profiles/BBL/machine/Bambu Lab X1 Carbon 0.4 nozzle.json @@ -26,8 +26,7 @@ "20", "20" ], - "machine_pause_gcode": "M400 U1\n", - "machine_start_gcode": ";===== machine: X1 =========================\n;===== date: 20230130 =====================\n;===== reset machine status =================\nG91\nM17 Z0.4 ; lower the z-motor current\nG0 Z12 F300 ; lower the hotbed , to prevent the nozzle is below the hotbed\nG0 Z-6;\nG90\nM17 X1.2 Y1.2 Z0.75 ; reset motor current to default\nM960 S5 P1 ; turn on logo lamp\nG90\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\nM73.2 R1.0 ;Reset left time magnitude\nM1002 set_gcode_claim_speed_level : 5\nM221 X0 Y0 Z0 ; turn off soft endstop to prevent protential logic problem\nG29.1 Z{+0.0} ; clear z-trim value first\n\n;===== heatbed preheat ====================\nM1002 gcode_claim_action : 2\nM140 S[bed_temperature_initial_layer_single] ;set bed temp\nM190 S[bed_temperature_initial_layer_single] ;wait for bed temp\n\n{if scan_first_layer}\n;=========register first layer scan=====\nM977 S1 P60\n{endif}\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_tool]==\"PLA\"}\n {if (bed_temperature[current_extruder] >45)||(bed_temperature_initial_layer[current_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[current_extruder] >50)||(bed_temperature_initial_layer[current_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n;===== prepare print temperature and material ==========\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp\nG91\nG0 Z10 F1200\nG90\nG28 X\nM975 S1 ; turn on \nG1 X60 F12000\nG1 Y245\nG1 Y265 F3000\nM620 M\nM620 S[initial_tool]A ; switch material if AMS exist\n M109 S[nozzle_temperature_initial_layer]\n G1 X120 F12000\n\n G1 X20 Y50 F12000\n G1 Y-3\n T[initial_tool]\n G1 X54 F12000\n G1 Y265\n M400\nM621 S[initial_tool]A\n\nM412 S1 ; ===turn on filament runout detection===\n\nM109 S250 ;set nozzle to common flush temp\nM106 P1 S0\nG92 E0\nG1 E50 F200\nM400\nM104 S[nozzle_temperature_initial_layer]\nG92 E0\nG1 E50 F200\nM400\nM106 P1 S255\nG92 E0\nG1 E5 F300\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20} ; drop nozzle temp, make filament shink a bit\nG92 E0\nG1 E-0.5 F300\n\nG1 X70 F9000\nG1 X76 F15000\nG1 X65 F15000\nG1 X76 F15000\nG1 X65 F15000; shake to put down garbage\nG1 X80 F6000\nG1 X95 F15000\nG1 X80 F15000\nG1 X165 F15000; wipe and shake\nM400\nM106 P1 S0\n;===== prepare print temperature and material end =====\n\n\n;===== wipe nozzle ===============================\nM1002 gcode_claim_action : 14\nM975 S1\nM106 S255\nG1 X65 Y230 F18000\nG1 Y264 F6000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20}\nG1 X100 F18000 ; first wipe mouth\n\nG0 X135 Y253 F20000 ; move to exposed steel surface edge\nG28 Z P0 T300; home z with low precision,permit 300deg temperature\nG29.2 S0 ; turn off ABL\nG0 Z5 F20000\n\nG1 X60 Y265\nG92 E0\nG1 E-0.5 F300 ; retrack more\nG1 X100 F5000; second wipe mouth\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X90 F5000\nG0 X128 Y261 Z-1.5 F20000 ; move to exposed steel surface and stop the nozzle\nM104 S140 ; set temp down to heatbed acceptable\nM106 S255 ; turn on fan (G28 has turn off fan)\n\nM221 S; push soft endstop status\nM221 Z0 ;turn off Z axis endstop\nG0 Z0.5 F20000\nG0 X125 Y259.5 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 X128\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\n\nM109 S140 ; wait nozzle temp down to heatbed acceptable\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\n\nM221 R; pop softend status\nG1 Z10 F1200\nM400\nG1 Z10\nG1 F30000\nG1 X128 Y128\nG29.2 S1 ; turn on ABL\n;G28 ; home again after hard wipe mouth\nM106 S0 ; turn off fan , too noisy\n;===== wipe nozzle end ================================\n\n;===== check scanner clarity ===========================\nG1 X128 Y128 F24000\nG28 Z P0\nM972 S5 P0\nG1 X230 Y15 F24000\n;===== check scanner clarity end =======================\n\n;===== bed leveling ==================================\nM1002 judge_flag g29_before_print_flag\nM622 J1\n\n M1002 gcode_claim_action : 1\n G29 A X{first_layer_print_min[0]} Y{first_layer_print_min[1]} I{first_layer_print_size[0]} J{first_layer_print_size[1]}\n M400\n M500 ; save cali data\n\nM623\n;===== bed leveling end ================================\n\n;===== home after wipe mouth============================\nM1002 judge_flag g29_before_print_flag\nM622 J0\n\n M1002 gcode_claim_action : 13\n G28\n\nM623\n;===== home after wipe mouth end =======================\n\nM975 S1 ; turn on vibration supression\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_tool]==\"PLA\"}\n {if (bed_temperature[current_extruder] >45)||(bed_temperature_initial_layer[current_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[current_extruder] >50)||(bed_temperature_initial_layer[current_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\nM104 S{nozzle_temperature_initial_layer[initial_extruder]} ; set extrude temp earlier, to reduce wait time\n\n;===== mech mode fast check============================\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q1 A7 B30 C80 H15 K0\nM974 Q1 S2 P0\n\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q0 A7 B30 C90 Q0 H15 K0\nM974 Q0 S2 P0\n\nM975 S1\nG1 F30000\nG1 X230 Y15\nG28 X ; re-home XY \n;===== mech mode fast check============================\n\n{if scan_first_layer}\n;start heatbed scan====================================\nM976 S2 P1 \nG90\nG1 X128 Y128 F20000\nM976 S3 P2 ;register void printing detection\n{endif}\n\n;===== noozle load line ===============================\nM975 S1\nG90 \nM83\nT1000\nG1 X18.0 Y1.0 Z0.8 F18000;Move to start position\nM109 S{nozzle_temperature[initial_extruder]}\nG1 Z0.2\nG0 E2 F300\nG0 X240 E15 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \nG0 Y11 E0.700 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\nG0 X239.5\nG0 E0.2\nG0 Y1.5 E0.700\nG0 X18 E15 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \nM400\n\n;===== for Textured PEI Plate , lower the nozzle as the nozzle was touching topmost of the texture when homing ==\n;curr_bed_type={curr_bed_type}\n{if curr_bed_type==\"Textured PEI Plate\"}\nG29.1 Z{-0.04} ; for Textured PEI Plate\n{endif}\n\n;===== draw extrinsic para cali paint =================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M1002 gcode_claim_action : 8\n\n T1000 \n G0 F3000 X28.000 Y19.500 Z0.200\n G1 F1200.0 X28.000 Y45.000 Z0.200 E0.933 \n G1 F1200.0 X28.500 Y45.000 Z0.200 E0.018 \n G1 F1200.0 X28.500 Y19.500 Z0.200 E0.933 \n G1 F1200.0 X31.000 Y19.500 Z0.200 E0.091 \n G1 F1200.0 X31.000 Y49.000 Z0.200 E1.080 \n G1 F1200.0 X37.500 Y49.000 Z0.200 E0.238 \n G1 F1200.0 X37.500 Y60.000 Z0.200 E0.403 \n G1 F1200.0 X42.500 Y60.000 Z0.200 E0.183 \n G1 F1200.0 X42.500 Y49.000 Z0.200 E0.403 \n G1 F1200.0 X48.000 Y49.000 Z0.200 E0.201 \n G1 F1200.0 X48.000 Y20.000 Z0.200 E1.061 \n G1 F1200.0 X30.000 Y20.000 Z0.200 E0.659 \n G1 F1200.0 X30.000 Y41.000 Z0.200 E0.769 \n G1 F1200.0 X50.000 Y41.000 Z0.200 E0.732 \n G1 F1200.0 X50.000 Y34.000 Z0.200 E0.256 \n G1 F1200.0 X30.000 Y34.000 Z0.200 E0.732 \n G1 F1500.000 E-0.800 \n\n ;=========== extruder cali extrusion ================== \n T1000 \n M83 \n {if default_acceleration > 0}\n {if outer_wall_acceleration > 0}\n M204 S[outer_wall_acceleration]\n {else}\n M204 S[default_acceleration]\n {endif}\n {endif}\n G0 X35.000 Y18.000 Z0.300 F30000 E0\n G1 F1500.000 E0.800 \n M106 S0 ; turn off fan\n G0 X185.000 E9.35441 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G0 X187 Z0\n G1 F1500.000 E-0.800 \n G0 Z1\n G0 X180 Z0.3 F18000\n \n M900 L1000.0 M1.0\n M900 K0.040 \n G0 X45.000 F30000 \n G0 Y20.000 F30000 \n G1 F1500.000 E0.800 \n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 F1500.000 E-0.800 \n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos \n G1 Z0.3\n M400\n\n G0 X45.000 F30000 \n M900 K0.020 \n G0 X45.000 F30000 \n G0 Y22.000 F30000 \n G1 F1500.000 E0.800 \n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 F1500.000 E-0.800 \n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos \n G1 Z0.3\n M400\n\n G0 X45.000 F30000 \n M900 K0.000 \n G0 X45.000 F30000 \n G0 Y24.000 F30000 \n G1 F1500.000 E0.800 \n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 F1500.000 E-0.800\n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos \n G1 Z0.3\n\n G0 X45.000 F30000 ; move to start point\n\nM623 ; end of \"draw extrinsic para cali paint\"\n\nM104 S140\n\n\n;=========== laser and rgb calibration =========== \nM400\nM18 E\nM500 R\n\nM973 S3 P14\n\nG1 X120 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\nT1100\nG1 X143.0 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\n\nM400 P100\n\nM960 S1 P1\nM400 P100\nM973 S6 P0; use auto exposure for horizontal laser by xcam\nM960 S0 P0\n\nG1 X240.0 Y6.0 Z0.3 F18000.0;Move to vertical extrude line pos\nM960 S2 P1\nM400 P100\nM973 S6 P1; use auto exposure for vertical laser by xcam\nM960 S0 P0\n\n;=========== handeye calibration ======================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M973 S3 P1 ; camera start stream\n M400 P500\n M973 S1 \n G0 F6000 X40.000 Y54.500 Z0.000 \n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500 \n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P1 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P200 \n M971 S5 P3 \n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P2 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P500 \n M971 S5 P4 \n M963 S1 \n M400 P1500 \n M964 \n T1100 \n G0 F6000 X40.000 Y54.500 Z0.000 \n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500 \n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P1 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P200 \n M971 S5 P3 \n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P2 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P500 \n M971 S5 P4 \n M963 S1 \n M400 P1500 \n M964 \n T1100 \n G1 Z3 F3000 \n\n M400\n M500 ; save cali data\n\n M104 S{nozzle_temperature[initial_extruder]} ; rise nozzle temp now ,to reduce temp waiting time.\n\n T1100 \n M400 P400 \n M960 S0 P0\n G0 F30000.000 Y22.000 X65.000 Z0.000\n M400 P400 \n M960 S1 P1 \n M400 P50 \n\n M969 S1 N3 A2000 \n G0 F360.000 X181.000 Z0.000\n M980.3 A70.000 B{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60/4} C5.000 D{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60} E5.000 F175.000 H1.000 I0.000 J0.020 K0.040\n M400 P100 \n G0 F20000\n G0 Z1 ; rise nozzle up\n T1000 ; change to nozzle space\n G0 X45.000 Y16.000 F30000 ; move to test line pos\n M969 S0 ; turn off scanning\n M960 S0 P0\n\n\n G1 Z2 F20000 \n T1000 \n G0 X45.000 Y16.000 F30000 E0\n M109 S{nozzle_temperature[initial_extruder]}\n G0 Z0.3\n G1 F1500.000 E3.600 \n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n\n ; see if extrude cali success, if not ,use default value\n M1002 judge_last_extrude_cali_success\n M622 J0\n M400\n M900 K0.02 M{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*0.02}\n M623 \n\n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X185.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X190.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X195.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X200.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X205.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X210.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X215.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X220.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X225.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n M973 S4 \n\nM623\n\n;========turn off light and wait extrude temperature =============\nM1002 gcode_claim_action : 0\nM973 S4 ; turn off scanner\nM400 ; wait all motion done before implement the emprical L parameters\n;M900 L500.0 ; Empirical parameters\nM109 S[nozzle_temperature_initial_layer]\nM960 S1 P0 ; turn off laser\nM960 S2 P0 ; turn off laser\nM106 S0 ; turn off fan\nM106 P2 S0 ; turn off big fan \nM106 P3 S0 ; turn off chamber fan\n\nM975 S1 ; turn on mech mode supression\nG90 \nM83\nT1000\nG1 E{-retraction_length[initial_extruder]} F1800\nG1 X128.0 Y253.0 Z0.2 F24000.0;Move to start position\nG1 E{retraction_length[initial_extruder]} F1800\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG0 X253 E6.4 F{outer_wall_volumetric_speed/(0.3*0.6) * 60} \nG0 Y128 E6.4\nG0 X252.5\nG0 Y252.5 E6.4\nG0 X128 E6.4", + "machine_start_gcode": ";===== machine: X1 =========================\n;===== date: 20230322 =====================\n;===== turn on the HB fan =================\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp to turn on the HB fan\n;===== reset machine status =================\nG91\nM17 Z0.4 ; lower the z-motor current\nG0 Z12 F300 ; lower the hotbed , to prevent the nozzle is below the hotbed\nG0 Z-6;\nG90\nM17 X1.2 Y1.2 Z0.75 ; reset motor current to default\nM960 S5 P1 ; turn on logo lamp\nG90\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\nM73.2 R1.0 ;Reset left time magnitude\nM1002 set_gcode_claim_speed_level : 5\nM221 X0 Y0 Z0 ; turn off soft endstop to prevent protential logic problem\nG29.1 Z{+0.0} ; clear z-trim value first\n\n;===== heatbed preheat ====================\nM1002 gcode_claim_action : 2\nM140 S[bed_temperature_initial_layer_single] ;set bed temp\nM190 S[bed_temperature_initial_layer_single] ;wait for bed temp\n\n{if scan_first_layer}\n;=========register first layer scan=====\nM977 S1 P60\n{endif}\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_no_support_extruder]==\"PLA\"}\n {if (bed_temperature[initial_no_support_extruder] >45)||(bed_temperature_initial_layer[initial_no_support_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[initial_no_support_extruder] >50)||(bed_temperature_initial_layer[initial_no_support_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n;===== prepare print temperature and material ==========\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp\nG91\nG0 Z10 F1200\nG90\nG28 X\nM975 S1 ; turn on \nG1 X60 F12000\nG1 Y245\nG1 Y265 F3000\nM620 M\nM620 S[initial_no_support_extruder]A ; switch material if AMS exist\n M109 S[nozzle_temperature_initial_layer]\n G1 X120 F12000\n\n G1 X20 Y50 F12000\n G1 Y-3\n T[initial_no_support_extruder]\n G1 X54 F12000\n G1 Y265\n M400\nM621 S[initial_no_support_extruder]A\nM620.1 E F{filament_max_volumetric_speed[initial_no_support_extruder]/2.4053*60} T{nozzle_temperature_range_high[initial_no_support_extruder]}\n\nM412 S1 ; ===turn on filament runout detection===\n\nM109 S250 ;set nozzle to common flush temp\nM106 P1 S0\nG92 E0\nG1 E50 F200\nM400\nM104 S[nozzle_temperature_initial_layer]\nG92 E0\nG1 E50 F200\nM400\nM106 P1 S255\nG92 E0\nG1 E5 F300\nM109 S{nozzle_temperature_initial_layer[initial_no_support_extruder]-20} ; drop nozzle temp, make filament shink a bit\nG92 E0\nG1 E-0.5 F300\n\nG1 X70 F9000\nG1 X76 F15000\nG1 X65 F15000\nG1 X76 F15000\nG1 X65 F15000; shake to put down garbage\nG1 X80 F6000\nG1 X95 F15000\nG1 X80 F15000\nG1 X165 F15000; wipe and shake\nM400\nM106 P1 S0\n;===== prepare print temperature and material end =====\n\n\n;===== wipe nozzle ===============================\nM1002 gcode_claim_action : 14\nM975 S1\nM106 S255\nG1 X65 Y230 F18000\nG1 Y264 F6000\nM109 S{nozzle_temperature_initial_layer[initial_no_support_extruder]-20}\nG1 X100 F18000 ; first wipe mouth\n\nG0 X135 Y253 F20000 ; move to exposed steel surface edge\nG28 Z P0 T300; home z with low precision,permit 300deg temperature\nG29.2 S0 ; turn off ABL\nG0 Z5 F20000\n\nG1 X60 Y265\nG92 E0\nG1 E-0.5 F300 ; retrack more\nG1 X100 F5000; second wipe mouth\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X90 F5000\nG0 X128 Y261 Z-1.5 F20000 ; move to exposed steel surface and stop the nozzle\nM104 S140 ; set temp down to heatbed acceptable\nM106 S255 ; turn on fan (G28 has turn off fan)\n\nM221 S; push soft endstop status\nM221 Z0 ;turn off Z axis endstop\nG0 Z0.5 F20000\nG0 X125 Y259.5 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 X128\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\n\nM109 S140 ; wait nozzle temp down to heatbed acceptable\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\n\nM221 R; pop softend status\nG1 Z10 F1200\nM400\nG1 Z10\nG1 F30000\nG1 X128 Y128\nG29.2 S1 ; turn on ABL\n;G28 ; home again after hard wipe mouth\nM106 S0 ; turn off fan , too noisy\n;===== wipe nozzle end ================================\n\n;===== check scanner clarity ===========================\nG1 X128 Y128 F24000\nG28 Z P0\nM972 S5 P0\nG1 X230 Y15 F24000\n;===== check scanner clarity end =======================\n\n;===== bed leveling ==================================\nM1002 judge_flag g29_before_print_flag\nM622 J1\n\n M1002 gcode_claim_action : 1\n G29 A X{first_layer_print_min[0]} Y{first_layer_print_min[1]} I{first_layer_print_size[0]} J{first_layer_print_size[1]}\n M400\n M500 ; save cali data\n\nM623\n;===== bed leveling end ================================\n\n;===== home after wipe mouth============================\nM1002 judge_flag g29_before_print_flag\nM622 J0\n\n M1002 gcode_claim_action : 13\n G28\n\nM623\n;===== home after wipe mouth end =======================\n\nM975 S1 ; turn on vibration supression\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_no_support_extruder]==\"PLA\"}\n {if (bed_temperature[initial_no_support_extruder] >45)||(bed_temperature_initial_layer[initial_no_support_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[initial_no_support_extruder] >50)||(bed_temperature_initial_layer[initial_no_support_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\nM104 S{nozzle_temperature_initial_layer[initial_no_support_extruder]} ; set extrude temp earlier, to reduce wait time\n\n;===== mech mode fast check============================\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q1 A7 B30 C80 H15 K0\nM974 Q1 S2 P0\n\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q0 A7 B30 C90 Q0 H15 K0\nM974 Q0 S2 P0\n\nM975 S1\nG1 F30000\nG1 X230 Y15\nG28 X ; re-home XY \n;===== mech mode fast check============================\n\n{if scan_first_layer}\n;start heatbed scan====================================\nM976 S2 P1 \nG90\nG1 X128 Y128 F20000\nM976 S3 P2 ;register void printing detection\n{endif}\n\n;===== noozle load line ===============================\nM975 S1\nG90 \nM83\nT1000\nG1 X18.0 Y1.0 Z0.8 F18000;Move to start position\nM109 S{nozzle_temperature[initial_no_support_extruder]}\nG1 Z0.2\nG0 E2 F300\nG0 X240 E15 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \nG0 Y11 E0.700 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\nG0 X239.5\nG0 E0.2\nG0 Y1.5 E0.700\nG0 X18 E15 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \nM400\n\n;===== for Textured PEI Plate , lower the nozzle as the nozzle was touching topmost of the texture when homing ==\n;curr_bed_type={curr_bed_type}\n{if curr_bed_type==\"Textured PEI Plate\"}\nG29.1 Z{-0.04} ; for Textured PEI Plate\n{endif}\n\n;===== draw extrinsic para cali paint =================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M1002 gcode_claim_action : 8\n\n T1000 \n G0 F3000 X28.000 Y19.500 Z0.200\n G1 F1200.0 X28.000 Y45.000 Z0.200 E0.933 \n G1 F1200.0 X28.500 Y45.000 Z0.200 E0.018 \n G1 F1200.0 X28.500 Y19.500 Z0.200 E0.933 \n G1 F1200.0 X31.000 Y19.500 Z0.200 E0.091 \n G1 F1200.0 X31.000 Y49.000 Z0.200 E1.080 \n G1 F1200.0 X37.500 Y49.000 Z0.200 E0.238 \n G1 F1200.0 X37.500 Y60.000 Z0.200 E0.403 \n G1 F1200.0 X42.500 Y60.000 Z0.200 E0.183 \n G1 F1200.0 X42.500 Y49.000 Z0.200 E0.403 \n G1 F1200.0 X48.000 Y49.000 Z0.200 E0.201 \n G1 F1200.0 X48.000 Y20.000 Z0.200 E1.061 \n G1 F1200.0 X30.000 Y20.000 Z0.200 E0.659 \n G1 F1200.0 X30.000 Y41.000 Z0.200 E0.769 \n G1 F1200.0 X50.000 Y41.000 Z0.200 E0.732 \n G1 F1200.0 X50.000 Y34.000 Z0.200 E0.256 \n G1 F1200.0 X30.000 Y34.000 Z0.200 E0.732 \n G1 F1500.000 E-0.800 \n\n ;=========== extruder cali extrusion ================== \n T1000 \n M83 \n {if default_acceleration > 0}\n {if outer_wall_acceleration > 0}\n M204 S[outer_wall_acceleration]\n {else}\n M204 S[default_acceleration]\n {endif}\n {endif}\n G0 X35.000 Y18.000 Z0.300 F30000 E0\n G1 F1500.000 E0.800 \n M106 S0 ; turn off fan\n G0 X185.000 E9.35441 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G0 X187 Z0\n G1 F1500.000 E-0.800 \n G0 Z1\n G0 X180 Z0.3 F18000\n \n M900 L1000.0 M1.0\n M900 K0.040 \n G0 X45.000 F30000 \n G0 Y20.000 F30000 \n G1 F1500.000 E0.800 \n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 F1500.000 E-0.800 \n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos \n G1 Z0.3\n M400\n\n G0 X45.000 F30000 \n M900 K0.020 \n G0 X45.000 F30000 \n G0 Y22.000 F30000 \n G1 F1500.000 E0.800 \n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 F1500.000 E-0.800 \n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos \n G1 Z0.3\n M400\n\n G0 X45.000 F30000 \n M900 K0.000 \n G0 X45.000 F30000 \n G0 Y24.000 F30000 \n G1 F1500.000 E0.800 \n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 F1500.000 E-0.800\n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos \n G1 Z0.3\n\n G0 X45.000 F30000 ; move to start point\n\nM623 ; end of \"draw extrinsic para cali paint\"\n\nM104 S140\n\n\n;=========== laser and rgb calibration =========== \nM400\nM18 E\nM500 R\n\nM973 S3 P14\n\nG1 X120 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\nT1100\nG1 X235.0 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\nM400 P100\nM960 S1 P1\nM400 P100\nM973 S6 P0; use auto exposure for horizontal laser by xcam\nM960 S0 P0\n\nG1 X240.0 Y6.0 Z0.3 F18000.0;Move to vertical extrude line pos\nM960 S2 P1\nM400 P100\nM973 S6 P1; use auto exposure for vertical laser by xcam\nM960 S0 P0\n\n;=========== handeye calibration ======================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M973 S3 P1 ; camera start stream\n M400 P500\n M973 S1 \n G0 F6000 X40.000 Y54.500 Z0.000 \n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500 \n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P1 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P200 \n M971 S5 P3 \n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P2 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P500 \n M971 S5 P4 \n M963 S1 \n M400 P1500 \n M964 \n T1100 \n G0 F6000 X40.000 Y54.500 Z0.000 \n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500 \n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P1 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P200 \n M971 S5 P3 \n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P2 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P500 \n M971 S5 P4 \n M963 S1 \n M400 P1500 \n M964 \n T1100 \n G1 Z3 F3000 \n\n M400\n M500 ; save cali data\n\n M104 S{nozzle_temperature[initial_no_support_extruder]} ; rise nozzle temp now ,to reduce temp waiting time.\n\n T1100 \n M400 P400 \n M960 S0 P0\n G0 F30000.000 Y22.000 X65.000 Z0.000\n M400 P400 \n M960 S1 P1 \n M400 P50 \n\n M969 S1 N3 A2000 \n G0 F360.000 X181.000 Z0.000\n M980.3 A70.000 B{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60/4} C5.000 D{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60} E5.000 F175.000 H1.000 I0.000 J0.020 K0.040\n M400 P100 \n G0 F20000\n G0 Z1 ; rise nozzle up\n T1000 ; change to nozzle space\n G0 X45.000 Y16.000 F30000 ; move to test line pos\n M969 S0 ; turn off scanning\n M960 S0 P0\n\n\n G1 Z2 F20000 \n T1000 \n G0 X45.000 Y16.000 F30000 E0\n M109 S{nozzle_temperature[initial_no_support_extruder]}\n G0 Z0.3\n G1 F1500.000 E3.600 \n G1 X65.000 E1.24726 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X75.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X80.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X85.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X90.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X95.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X100.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X105.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X110.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X115.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X120.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X125.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X130.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X135.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n\n ; see if extrude cali success, if not ,use default value\n M1002 judge_last_extrude_cali_success\n M622 J0\n M400\n M900 K0.02 M{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*0.02}\n M623 \n\n G1 X140.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X145.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X150.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X155.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X160.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X165.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X170.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X175.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X180.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X185.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X190.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X195.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X200.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X205.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X210.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X215.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X220.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X225.000 E0.31181 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n M973 S4 \n\nM623\n\n;========turn off light and wait extrude temperature =============\nM1002 gcode_claim_action : 0\nM973 S4 ; turn off scanner\nM400 ; wait all motion done before implement the emprical L parameters\n;M900 L500.0 ; Empirical parameters\nM109 S[nozzle_temperature_initial_layer]\nM960 S1 P0 ; turn off laser\nM960 S2 P0 ; turn off laser\nM106 S0 ; turn off fan\nM106 P2 S0 ; turn off big fan \nM106 P3 S0 ; turn off chamber fan\n\nM975 S1 ; turn on mech mode supression\nG90 \nM83\nT1000\nG1 E{-retraction_length[initial_no_support_extruder]} F1800\nG1 X128.0 Y253.0 Z0.2 F24000.0;Move to start position\nG1 E{retraction_length[initial_no_support_extruder]} F1800\nM109 S{nozzle_temperature_initial_layer[initial_no_support_extruder]}\nG0 X253 E6.4 F{outer_wall_volumetric_speed/(0.3*0.6) * 60} \nG0 Y128 E6.4\nG0 X252.5\nG0 Y252.5 E6.4\nG0 X128 E6.4", "scan_first_layer": "1", "machine_load_filament_time": "29", "machine_unload_filament_time": "28", diff --git a/resources/profiles/BBL/machine/Bambu Lab X1 Carbon 0.6 nozzle.json b/resources/profiles/BBL/machine/Bambu Lab X1 Carbon 0.6 nozzle.json index 8600d18f28..53ebc5f479 100644 --- a/resources/profiles/BBL/machine/Bambu Lab X1 Carbon 0.6 nozzle.json +++ b/resources/profiles/BBL/machine/Bambu Lab X1 Carbon 0.6 nozzle.json @@ -26,7 +26,7 @@ "retraction_minimum_travel": [ "3" ], - "machine_start_gcode": ";===== machine: X1 =========================\n;===== date: 20230130 =====================\n;===== reset machine status =================\nG91\nM17 Z0.4 ; lower the z-motor current\nG0 Z12 F300 ; lower the hotbed , to prevent the nozzle is below the hotbed\nG0 Z-6;\nG90\nM17 X1.2 Y1.2 Z0.75 ; reset motor current to default\nM960 S5 P1 ; turn on logo lamp\nG90\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\nM73.2 R1.0 ;Reset left time magnitude\nM1002 set_gcode_claim_speed_level : 5\nM221 X0 Y0 Z0 ; turn off soft endstop to prevent protential logic problem\nG29.1 Z{+0.0} ; clear z-trim value first\n\n;===== heatbed preheat ====================\nM1002 gcode_claim_action : 2\nM140 S[bed_temperature_initial_layer_single] ;set bed temp\nM190 S[bed_temperature_initial_layer_single] ;wait for bed temp\n\n{if scan_first_layer}\n;=========register first layer scan=====\nM977 S1 P60\n{endif}\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_tool]==\"PLA\"}\n {if (bed_temperature[current_extruder] >45)||(bed_temperature_initial_layer[current_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[current_extruder] >50)||(bed_temperature_initial_layer[current_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n;===== prepare print temperature and material ==========\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp\nG91\nG0 Z10 F1200\nG90\nG28 X\nM975 S1 ; turn on \nG1 X60 F12000\nG1 Y245\nG1 Y265 F3000\nM620 M\nM620 S[initial_tool]A ; switch material if AMS exist\n M109 S[nozzle_temperature_initial_layer]\n G1 X120 F12000\n\n G1 X20 Y50 F12000\n G1 Y-3\n T[initial_tool]\n G1 X54 F12000\n G1 Y265\n M400\nM621 S[initial_tool]A\n\nM412 S1 ; ===turn on filament runout detection===\n\nM109 S250 ;set nozzle to common flush temp\nM106 P1 S0\nG92 E0\nG1 E50 F200\nM400\nM104 S[nozzle_temperature_initial_layer]\nG92 E0\nG1 E50 F200\nM400\nM106 P1 S255\nG92 E0\nG1 E5 F300\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20} ; drop nozzle temp, make filament shink a bit\nG92 E0\nG1 E-0.5 F300\n\nG1 X70 F9000\nG1 X76 F15000\nG1 X65 F15000\nG1 X76 F15000\nG1 X65 F15000; shake to put down garbage\nG1 X80 F6000\nG1 X95 F15000\nG1 X80 F15000\nG1 X165 F15000; wipe and shake\nM400\nM106 P1 S0\n;===== prepare print temperature and material end =====\n\n\n;===== wipe nozzle ===============================\nM1002 gcode_claim_action : 14\nM975 S1\nM106 S255\nG1 X65 Y230 F18000\nG1 Y264 F6000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20}\nG1 X100 F18000 ; first wipe mouth\n\nG0 X135 Y253 F20000 ; move to exposed steel surface edge\nG28 Z P0 T300; home z with low precision,permit 300deg temperature\nG29.2 S0 ; turn off ABL\nG0 Z5 F20000\n\nG1 X60 Y265\nG92 E0\nG1 E-0.5 F300 ; retrack more\nG1 X100 F5000; second wipe mouth\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X90 F5000\nG0 X128 Y261 Z-1.5 F20000 ; move to exposed steel surface and stop the nozzle\nM104 S140 ; set temp down to heatbed acceptable\nM106 S255 ; turn on fan (G28 has turn off fan)\n\nM221 S; push soft endstop status\nM221 Z0 ;turn off Z axis endstop\nG0 Z0.5 F20000\nG0 X125 Y259.5 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 X128\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\n\nM109 S140 ; wait nozzle temp down to heatbed acceptable\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\n\nM221 R; pop softend status\nG1 Z10 F1200\nM400\nG1 Z10\nG1 F30000\nG1 X128 Y128\nG29.2 S1 ; turn on ABL\n;G28 ; home again after hard wipe mouth\nM106 S0 ; turn off fan , too noisy\n;===== wipe nozzle end ================================\n\n;===== check scanner clarity ===========================\nG1 X128 Y128 F24000\nG28 Z P0\nM972 S5 P0\nG1 X230 Y15 F24000\n;===== check scanner clarity end =======================\n\n;===== bed leveling ==================================\nM1002 judge_flag g29_before_print_flag\nM622 J1\n\n M1002 gcode_claim_action : 1\n G29 A X{first_layer_print_min[0]} Y{first_layer_print_min[1]} I{first_layer_print_size[0]} J{first_layer_print_size[1]}\n M400\n M500 ; save cali data\n\nM623\n;===== bed leveling end ================================\n\n;===== home after wipe mouth============================\nM1002 judge_flag g29_before_print_flag\nM622 J0\n\n M1002 gcode_claim_action : 13\n G28\n\nM623\n;===== home after wipe mouth end =======================\n\nM975 S1 ; turn on vibration supression\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_tool]==\"PLA\"}\n {if (bed_temperature[current_extruder] >45)||(bed_temperature_initial_layer[current_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[current_extruder] >50)||(bed_temperature_initial_layer[current_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\nM104 S{nozzle_temperature_initial_layer[initial_extruder]} ; set extrude temp earlier, to reduce wait time\n\n;===== mech mode fast check============================\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q1 A7 B30 C80 H15 K0\nM974 Q1 S2 P0\n\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q0 A7 B30 C90 Q0 H15 K0\nM974 Q0 S2 P0\n\nM975 S1\nG1 F30000\nG1 X230 Y15\nG28 X ; re-home XY \n;===== mech mode fast check============================\n\n{if scan_first_layer}\n;start heatbed scan====================================\nM976 S2 P1 \nG90\nG1 X128 Y128 F20000\nM976 S3 P2 ;register void printing detection\n{endif}\n\n;===== noozle load line ===============================\nM975 S1\nG90 \nM83\nT1000\nG1 X18.0 Y1.0 Z0.8 F18000;Move to start position\nM109 S{nozzle_temperature[initial_extruder]}\nG1 Z0.2\nG0 E2 F300\nG0 X240 E25 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \nG0 Y15 E1.166 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\nG0 X239.5\nG0 E0.2\nG0 Y1.5 E1.166\nG0 X18 E25 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \nM400\n\n;===== for Textured PEI Plate , lower the nozzle as the nozzle was touching topmost of the texture when homing ==\n;curr_bed_type={curr_bed_type}\n{if curr_bed_type==\"Textured PEI Plate\"}\nG29.1 Z{-0.04} ; for Textured PEI Plate\n{endif}\n\n;===== draw extrinsic para cali paint =================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M1002 gcode_claim_action : 8\n\n T1000 \n G0 F3000 X28.000 Y19.500 Z0.200\n G1 F1200.0 X28.000 Y45.000 Z0.200 E1.679 \n G1 F1200.0 X28.500 Y45.000 Z0.200 E0.032 \n G1 F1200.0 X28.500 Y19.500 Z0.200 E1.679 \n G1 F1200.0 X31.000 Y19.500 Z0.200 E0.164 \n G1 F1200.0 X31.000 Y49.000 Z0.200 E1.944 \n G1 F1200.0 X37.500 Y49.000 Z0.200 E0.428 \n G1 F1200.0 X37.500 Y60.000 Z0.200 E0.725 \n G1 F1200.0 X42.500 Y60.000 Z0.200 E0.329 \n G1 F1200.0 X42.500 Y49.000 Z0.200 E0.725 \n G1 F1200.0 X48.000 Y49.000 Z0.200 E0.362 \n G1 F1200.0 X48.000 Y20.000 Z0.200 E1.910 \n G1 F1200.0 X30.000 Y20.000 Z0.200 E1.186 \n G1 F1200.0 X30.000 Y41.000 Z0.200 E1.384 \n G1 F1200.0 X50.000 Y41.000 Z0.200 E1.318 \n G1 F1200.0 X50.000 Y34.000 Z0.200 E0.461 \n G1 F1200.0 X30.000 Y34.000 Z0.200 E1.318 \n G1 F1500.000 E-0.800 \n\n ;=========== extruder cali extrusion ================== \n T1000 \n M83 \n {if default_acceleration > 0}\n {if outer_wall_acceleration > 0}\n M204 S[outer_wall_acceleration]\n {else}\n M204 S[default_acceleration]\n {endif}\n {endif}\n G0 X35.000 Y18.000 Z0.300 F30000 E0\n G1 F1500.000 E0.800 \n M106 S0 ; turn off fan\n G0 X185.000 E16.9 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G0 X187 Z0\n G1 F1500.000 E-0.800 \n G0 Z1\n G0 X180 Z0.3 F18000\n \n M900 L1000.0 M1.0\n M900 K0.030 \n G0 X45.000 F30000 \n G0 Y20.000 F30000 \n G1 F1500.000 E0.800 \n G1 X65.000 E2.25000 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X70.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X75.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X80.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X85.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X90.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X95.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X100.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X105.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X110.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X115.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X120.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X125.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X130.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X135.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X140.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X145.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X150.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X155.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X160.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X165.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X170.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X175.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X180.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 F1500.000 E-0.800 \n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos \n G1 Z0.3\n M400\n\n G0 X45.000 F30000 \n M900 K0.015 \n G0 X45.000 F30000 \n G0 Y22.000 F30000 \n G1 F1500.000 E0.800 \n G1 X65.000 E2.25000 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X70.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X75.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X80.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X85.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X90.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X95.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X100.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X105.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X110.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X115.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X120.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X125.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X130.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X135.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X140.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X145.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X150.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X155.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X160.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X165.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X170.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X175.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X180.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 F1500.000 E-0.800 \n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos \n G1 Z0.3\n M400\n\n G0 X45.000 F30000 \n M900 K0.000 \n G0 X45.000 F30000 \n G0 Y24.000 F30000 \n G1 F1500.000 E0.800 \n G1 X65.000 E2.25000 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X70.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X75.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X80.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X85.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X90.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X95.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X100.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X105.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X110.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X115.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X120.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X125.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X130.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X135.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X140.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X145.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X150.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X155.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X160.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X165.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X170.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60} \n G1 X175.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 X180.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60} \n G1 F1500.000 E-0.800\n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos \n G1 Z0.3\n\n G0 X45.000 F30000 ; move to start point\n\nM623 ; end of \"draw extrinsic para cali paint\"\n\nM104 S140\n\n\n;=========== laser and rgb calibration =========== \nM400\nM18 E\nM500 R\n\nM973 S3 P14\n\nG1 X120 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\nT1100\nG1 X143.0 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\n\nM400 P100\n\nM960 S1 P1\nM400 P100\nM973 S6 P0; use auto exposure for horizontal laser by xcam\nM960 S0 P0\n\nG1 X240.0 Y6.0 Z0.3 F18000.0;Move to vertical extrude line pos\nM960 S2 P1\nM400 P100\nM973 S6 P1; use auto exposure for vertical laser by xcam\nM960 S0 P0\n\n;=========== handeye calibration ======================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M973 S3 P1 ; camera start stream\n M400 P500\n M973 S1 \n G0 F6000 X40.000 Y54.500 Z0.000 \n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500 \n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P1 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P200 \n M971 S5 P3 \n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P2 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P500 \n M971 S5 P4 \n M963 S1 \n M400 P1500 \n M964 \n T1100 \n G0 F6000 X40.000 Y54.500 Z0.000 \n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500 \n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P1 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P200 \n M971 S5 P3 \n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P2 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P500 \n M971 S5 P4 \n M963 S1 \n M400 P1500 \n M964 \n T1100 \n G1 Z3 F3000 \n\n M400\n M500 ; save cali data\n\n M104 S{nozzle_temperature[initial_extruder]} ; rise nozzle temp now ,to reduce temp waiting time.\n\n T1100 \n M400 P400 \n M960 S0 P0\n G0 F30000.000 Y22.000 X65.000 Z0.000\n M400 P400 \n M960 S1 P1 \n M400 P50 \n\n M969 S1 N3 A2000 \n G0 F360.000 X181.000 Z0.000\n M980.3 A70.000 B{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60/4} C5.000 D{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60} E5.000 F175.000 H1.000 I0.000 J0.015 K0.030\n M400 P100 \n G0 F20000\n G0 Z1 ; rise nozzle up\n T1000 ; change to nozzle space\n G0 X45.000 Y16.000 F30000 ; move to test line pos\n M969 S0 ; turn off scanning\n M960 S0 P0\n\n\n G1 Z2 F20000 \n T1000 \n G0 X45.000 Y16.000 F30000 E0\n M109 S{nozzle_temperature[initial_extruder]}\n G0 Z0.3\n G1 F1500.000 E3.600 \n G1 X65.000 E2.25000 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X75.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X80.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X85.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X90.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X95.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X100.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X105.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X110.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X115.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X120.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X125.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X130.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X135.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n\n ; see if extrude cali success, if not ,use default value\n M1002 judge_last_extrude_cali_success\n M622 J0\n M400\n M900 K0.015 M{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*0.015}\n M623 \n\n G1 X140.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X145.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X150.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X155.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X160.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X165.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X170.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X175.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X180.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X185.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X190.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X195.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X200.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X205.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X210.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X215.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n G1 X220.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60} \n G1 X225.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60} \n M973 S4 \n\nM623\n\n;========turn off light and wait extrude temperature =============\nM1002 gcode_claim_action : 0\nM973 S4 ; turn off scanner\nM400 ; wait all motion done before implement the emprical L parameters\n;M900 L500.0 ; Empirical parameters\nM109 S[nozzle_temperature_initial_layer]\nM960 S1 P0 ; turn off laser\nM960 S2 P0 ; turn off laser\nM106 S0 ; turn off fan\nM106 P2 S0 ; turn off big fan \nM106 P3 S0 ; turn off chamber fan\n\nM975 S1 ; turn on mech mode supression\nG90 \nM83\nT1000\nG1 E{-retraction_length[initial_extruder]} F1800\nG1 X128.0 Y253.0 Z0.2 F24000.0;Move to start position\nG1 E{retraction_length[initial_extruder]} F1800\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG0 X253 E6.4 F{outer_wall_volumetric_speed/(0.3*0.6) * 60} \nG0 Y128 E6.4\nG0 X252.5\nG0 Y252.5 E6.4\nG0 X128 E6.4", + "machine_start_gcode": ";===== machine: X1 =========================\n;===== date: 20230322 =====================\n;===== turn on the HB fan =================\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp to turn on the HB fan\n;===== reset machine status =================\nG91\nM17 Z0.4 ; lower the z-motor current\nG0 Z12 F300 ; lower the hotbed , to prevent the nozzle is below the hotbed\nG0 Z-6;\nG90\nM17 X1.2 Y1.2 Z0.75 ; reset motor current to default\nM960 S5 P1 ; turn on logo lamp\nG90\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\nM73.2 R1.0 ;Reset left time magnitude\nM1002 set_gcode_claim_speed_level : 5\nM221 X0 Y0 Z0 ; turn off soft endstop to prevent protential logic problem\nG29.1 Z{+0.0} ; clear z-trim value first\n\n;===== heatbed preheat ====================\nM1002 gcode_claim_action : 2\nM140 S[bed_temperature_initial_layer_single] ;set bed temp\nM190 S[bed_temperature_initial_layer_single] ;wait for bed temp\n\n{if scan_first_layer}\n;=========register first layer scan=====\nM977 S1 P60\n{endif}\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_no_support_extruder]==\"PLA\"}\n {if (bed_temperature[initial_no_support_extruder] >45)||(bed_temperature_initial_layer[initial_no_support_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[initial_no_support_extruder] >50)||(bed_temperature_initial_layer[initial_no_support_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n;===== prepare print temperature and material ==========\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp\nG91\nG0 Z10 F1200\nG90\nG28 X\nM975 S1 ; turn on\nG1 X60 F12000\nG1 Y245\nG1 Y265 F3000\nM620 M\nM620 S[initial_no_support_extruder]A ; switch material if AMS exist\n M109 S[nozzle_temperature_initial_layer]\n G1 X120 F12000\n\n G1 X20 Y50 F12000\n G1 Y-3\n T[initial_no_support_extruder]\n G1 X54 F12000\n G1 Y265\n M400\nM621 S[initial_no_support_extruder]A\nM620.1 E F{filament_max_volumetric_speed[initial_no_support_extruder]/2.4053*60} T{nozzle_temperature_range_high[initial_no_support_extruder]}\n\n\nM412 S1 ; ===turn on filament runout detection===\n\nM109 S250 ;set nozzle to common flush temp\nM106 P1 S0\nG92 E0\nG1 E50 F200\nM400\nM104 S[nozzle_temperature_initial_layer]\nG92 E0\nG1 E50 F200\nM400\nM106 P1 S255\nG92 E0\nG1 E5 F300\nM109 S{nozzle_temperature_initial_layer[initial_no_support_extruder]-20} ; drop nozzle temp, make filament shink a bit\nG92 E0\nG1 E-0.5 F300\n\nG1 X70 F9000\nG1 X76 F15000\nG1 X65 F15000\nG1 X76 F15000\nG1 X65 F15000; shake to put down garbage\nG1 X80 F6000\nG1 X95 F15000\nG1 X80 F15000\nG1 X165 F15000; wipe and shake\nM400\nM106 P1 S0\n;===== prepare print temperature and material end =====\n\n\n;===== wipe nozzle ===============================\nM1002 gcode_claim_action : 14\nM975 S1\nM106 S255\nG1 X65 Y230 F18000\nG1 Y264 F6000\nM109 S{nozzle_temperature_initial_layer[initial_no_support_extruder]-20}\nG1 X100 F18000 ; first wipe mouth\n\nG0 X135 Y253 F20000 ; move to exposed steel surface edge\nG28 Z P0 T300; home z with low precision,permit 300deg temperature\nG29.2 S0 ; turn off ABL\nG0 Z5 F20000\n\nG1 X60 Y265\nG92 E0\nG1 E-0.5 F300 ; retrack more\nG1 X100 F5000; second wipe mouth\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X90 F5000\nG0 X128 Y261 Z-1.5 F20000 ; move to exposed steel surface and stop the nozzle\nM104 S140 ; set temp down to heatbed acceptable\nM106 S255 ; turn on fan (G28 has turn off fan)\n\nM221 S; push soft endstop status\nM221 Z0 ;turn off Z axis endstop\nG0 Z0.5 F20000\nG0 X125 Y259.5 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 X128\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\n\nM109 S140 ; wait nozzle temp down to heatbed acceptable\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\n\nM221 R; pop softend status\nG1 Z10 F1200\nM400\nG1 Z10\nG1 F30000\nG1 X128 Y128\nG29.2 S1 ; turn on ABL\n;G28 ; home again after hard wipe mouth\nM106 S0 ; turn off fan , too noisy\n;===== wipe nozzle end ================================\n\n;===== check scanner clarity ===========================\nG1 X128 Y128 F24000\nG28 Z P0\nM972 S5 P0\nG1 X230 Y15 F24000\n;===== check scanner clarity end =======================\n\n;===== bed leveling ==================================\nM1002 judge_flag g29_before_print_flag\nM622 J1\n\n M1002 gcode_claim_action : 1\n G29 A X{first_layer_print_min[0]} Y{first_layer_print_min[1]} I{first_layer_print_size[0]} J{first_layer_print_size[1]}\n M400\n M500 ; save cali data\n\nM623\n;===== bed leveling end ================================\n\n;===== home after wipe mouth============================\nM1002 judge_flag g29_before_print_flag\nM622 J0\n\n M1002 gcode_claim_action : 13\n G28\n\nM623\n;===== home after wipe mouth end =======================\n\nM975 S1 ; turn on vibration supression\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_no_support_extruder]==\"PLA\"}\n {if (bed_temperature[initial_no_support_extruder] >45)||(bed_temperature_initial_layer[initial_no_support_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[initial_no_support_extruder] >50)||(bed_temperature_initial_layer[initial_no_support_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\nM104 S{nozzle_temperature_initial_layer[initial_no_support_extruder]} ; set extrude temp earlier, to reduce wait time\n\n;===== mech mode fast check============================\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q1 A7 B30 C80 H15 K0\nM974 Q1 S2 P0\n\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q0 A7 B30 C90 Q0 H15 K0\nM974 Q0 S2 P0\n\nM975 S1\nG1 F30000\nG1 X230 Y15\nG28 X ; re-home XY\n;===== mech mode fast check============================\n\n{if scan_first_layer}\n;start heatbed scan====================================\nM976 S2 P1\nG90\nG1 X128 Y128 F20000\nM976 S3 P2 ;register void printing detection\n{endif}\n\n;===== noozle load line ===============================\nM975 S1\nG90\nM83\nT1000\nG1 X18.0 Y1.0 Z0.8 F18000;Move to start position\nM109 S{nozzle_temperature[initial_no_support_extruder]}\nG1 Z0.2\nG0 E2 F300\nG0 X240 E25 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\nG0 Y15 E1.166 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\nG0 X239.5\nG0 E0.2\nG0 Y1.5 E1.166\nG0 X18 E25 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\nM400\n\n;===== for Textured PEI Plate , lower the nozzle as the nozzle was touching topmost of the texture when homing ==\n;curr_bed_type={curr_bed_type}\n{if curr_bed_type==\"Textured PEI Plate\"}\nG29.1 Z{-0.04} ; for Textured PEI Plate\n{endif}\n\n;===== draw extrinsic para cali paint =================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M1002 gcode_claim_action : 8\n\n T1000\n G0 F3000 X28.000 Y19.500 Z0.200\n G1 F1200.0 X28.000 Y45.000 Z0.200 E1.679\n G1 F1200.0 X28.500 Y45.000 Z0.200 E0.032\n G1 F1200.0 X28.500 Y19.500 Z0.200 E1.679\n G1 F1200.0 X31.000 Y19.500 Z0.200 E0.164\n G1 F1200.0 X31.000 Y49.000 Z0.200 E1.944\n G1 F1200.0 X37.500 Y49.000 Z0.200 E0.428\n G1 F1200.0 X37.500 Y60.000 Z0.200 E0.725\n G1 F1200.0 X42.500 Y60.000 Z0.200 E0.329\n G1 F1200.0 X42.500 Y49.000 Z0.200 E0.725\n G1 F1200.0 X48.000 Y49.000 Z0.200 E0.362\n G1 F1200.0 X48.000 Y20.000 Z0.200 E1.910\n G1 F1200.0 X30.000 Y20.000 Z0.200 E1.186\n G1 F1200.0 X30.000 Y41.000 Z0.200 E1.384\n G1 F1200.0 X50.000 Y41.000 Z0.200 E1.318\n G1 F1200.0 X50.000 Y34.000 Z0.200 E0.461\n G1 F1200.0 X30.000 Y34.000 Z0.200 E1.318\n G1 F1500.000 E-0.800\n\n ;=========== extruder cali extrusion ==================\n T1000\n M83\n {if default_acceleration > 0}\n {if outer_wall_acceleration > 0}\n M204 S[outer_wall_acceleration]\n {else}\n M204 S[default_acceleration]\n {endif}\n {endif}\n G0 X35.000 Y18.000 Z0.300 F30000 E0\n G1 F1500.000 E0.800\n M106 S0 ; turn off fan\n G0 X185.000 E16.9 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G0 X187 Z0\n G1 F1500.000 E-0.800\n G0 Z1\n G0 X180 Z0.3 F18000\n\n M900 L1000.0 M1.0\n M900 K0.030\n G0 X45.000 F30000\n G0 Y20.000 F30000\n G1 F1500.000 E0.800\n G1 X65.000 E2.25000 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X70.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X75.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X80.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X85.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X90.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X95.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X100.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X105.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X110.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X115.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X120.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X125.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X130.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X135.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X140.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X145.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X150.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X155.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X160.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X165.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X170.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X175.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X180.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 F1500.000 E-0.800\n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos\n G1 Z0.3\n M400\n\n G0 X45.000 F30000\n M900 K0.015\n G0 X45.000 F30000\n G0 Y22.000 F30000\n G1 F1500.000 E0.800\n G1 X65.000 E2.25000 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X70.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X75.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X80.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X85.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X90.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X95.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X100.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X105.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X110.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X115.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X120.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X125.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X130.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X135.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X140.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X145.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X150.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X155.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X160.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X165.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X170.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X175.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X180.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 F1500.000 E-0.800\n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos\n G1 Z0.3\n M400\n\n G0 X45.000 F30000\n M900 K0.000\n G0 X45.000 F30000\n G0 Y24.000 F30000\n G1 F1500.000 E0.800\n G1 X65.000 E2.25000 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X70.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X75.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X80.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X85.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X90.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X95.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X100.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X105.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X110.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X115.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X120.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X125.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X130.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X135.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X140.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X145.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X150.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X155.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X160.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X165.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X170.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9)/ 4 * 60}\n G1 X175.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 X180.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.9) * 60}\n G1 F1500.000 E-0.800\n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos\n G1 Z0.3\n\n G0 X45.000 F30000 ; move to start point\n\nM623 ; end of \"draw extrinsic para cali paint\"\n\nM104 S140\n\n\n;=========== laser and rgb calibration ===========\nM400\nM18 E\nM500 R\n\nM973 S3 P14\n\nG1 X120 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\nT1100\nG1 X235.0 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\nM400 P100\nM960 S1 P1\nM400 P100\nM973 S6 P0; use auto exposure for horizontal laser by xcam\nM960 S0 P0\n\nG1 X240.0 Y6.0 Z0.3 F18000.0;Move to vertical extrude line pos\nM960 S2 P1\nM400 P100\nM973 S6 P1; use auto exposure for vertical laser by xcam\nM960 S0 P0\n\n;=========== handeye calibration ======================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M973 S3 P1 ; camera start stream\n M400 P500\n M973 S1\n G0 F6000 X40.000 Y54.500 Z0.000\n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500\n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1\n G0 Y37.50\n M400 P200\n M971 S5 P1\n M960 S0 P0\n M960 S2 P1\n G0 Y54.50\n M400 P200\n M971 S5 P3\n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1\n G0 Y37.50\n M400 P200\n M971 S5 P2\n M960 S0 P0\n M960 S2 P1\n G0 Y54.50\n M400 P500\n M971 S5 P4\n M963 S1\n M400 P1500\n M964\n T1100\n G0 F6000 X40.000 Y54.500 Z0.000\n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500\n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1\n G0 Y37.50\n M400 P200\n M971 S5 P1\n M960 S0 P0\n M960 S2 P1\n G0 Y54.50\n M400 P200\n M971 S5 P3\n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1\n G0 Y37.50\n M400 P200\n M971 S5 P2\n M960 S0 P0\n M960 S2 P1\n G0 Y54.50\n M400 P500\n M971 S5 P4\n M963 S1\n M400 P1500\n M964\n T1100\n G1 Z3 F3000\n\n M400\n M500 ; save cali data\n\n M104 S{nozzle_temperature[initial_no_support_extruder]} ; rise nozzle temp now ,to reduce temp waiting time.\n\n T1100\n M400 P400\n M960 S0 P0\n G0 F30000.000 Y22.000 X65.000 Z0.000\n M400 P400\n M960 S1 P1\n M400 P50\n\n M969 S1 N3 A2000\n G0 F360.000 X181.000 Z0.000\n M980.3 A70.000 B{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60/4} C5.000 D{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60} E5.000 F175.000 H1.000 I0.000 J0.015 K0.030\n M400 P100\n G0 F20000\n G0 Z1 ; rise nozzle up\n T1000 ; change to nozzle space\n G0 X45.000 Y16.000 F30000 ; move to test line pos\n M969 S0 ; turn off scanning\n M960 S0 P0\n\n\n G1 Z2 F20000\n T1000\n G0 X45.000 Y16.000 F30000 E0\n M109 S{nozzle_temperature[initial_no_support_extruder]}\n G0 Z0.3\n G1 F1500.000 E3.600\n G1 X65.000 E2.25000 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X70.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X75.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X80.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X85.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X90.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X95.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X100.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X105.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X110.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X115.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X120.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X125.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X130.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X135.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n\n ; see if extrude cali success, if not ,use default value\n M1002 judge_last_extrude_cali_success\n M622 J0\n M400\n M900 K0.015 M{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*0.015}\n M623\n\n G1 X140.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X145.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X150.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X155.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X160.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X165.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X170.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X175.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X180.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X185.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X190.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X195.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X200.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X205.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X210.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X215.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n G1 X220.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5)/ 4 * 60}\n G1 X225.000 E0.56250 F{outer_wall_volumetric_speed/(0.3*0.5) * 60}\n M973 S4\n\nM623\n\n;========turn off light and wait extrude temperature =============\nM1002 gcode_claim_action : 0\nM973 S4 ; turn off scanner\nM400 ; wait all motion done before implement the emprical L parameters\n;M900 L500.0 ; Empirical parameters\nM109 S[nozzle_temperature_initial_layer]\nM960 S1 P0 ; turn off laser\nM960 S2 P0 ; turn off laser\nM106 S0 ; turn off fan\nM106 P2 S0 ; turn off big fan\nM106 P3 S0 ; turn off chamber fan\n\nM975 S1 ; turn on mech mode supression\nG90\nM83\nT1000\nG1 E{-retraction_length[initial_no_support_extruder]} F1800\nG1 X128.0 Y253.0 Z0.2 F24000.0;Move to start position\nG1 E{retraction_length[initial_no_support_extruder]} F1800\nM109 S{nozzle_temperature_initial_layer[initial_no_support_extruder]}\nG0 X253 E6.4 F{outer_wall_volumetric_speed/(0.3*0.6) * 60}\nG0 Y128 E6.4\nG0 X252.5\nG0 Y252.5 E6.4\nG0 X128 E6.4", "upward_compatible_machine":[ "Bambu Lab P1P 0.6 nozzle", "Bambu Lab X1 0.6 nozzle" diff --git a/resources/profiles/BBL/machine/Bambu Lab X1 Carbon 0.8 nozzle.json b/resources/profiles/BBL/machine/Bambu Lab X1 Carbon 0.8 nozzle.json index 062e5b5522..1da5d4cb6e 100644 --- a/resources/profiles/BBL/machine/Bambu Lab X1 Carbon 0.8 nozzle.json +++ b/resources/profiles/BBL/machine/Bambu Lab X1 Carbon 0.8 nozzle.json @@ -29,7 +29,7 @@ "retract_length_toolchange": [ "3" ], - "machine_start_gcode": ";===== machine: X1 =========================\n;===== date: 20230130 =====================\n;===== reset machine status =================\nG91\nM17 Z0.4 ; lower the z-motor current\nG0 Z12 F300 ; lower the hotbed , to prevent the nozzle is below the hotbed\nG0 Z-6;\nG90\nM17 X1.2 Y1.2 Z0.75 ; reset motor current to default\nM960 S5 P1 ; turn on logo lamp\nG90\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\nM73.2 R1.0 ;Reset left time magnitude\nM1002 set_gcode_claim_speed_level : 5\nM221 X0 Y0 Z0 ; turn off soft endstop to prevent protential logic problem\nG29.1 Z{0.0} ; clear z-trim value first\n\n;===== heatbed preheat ====================\nM1002 gcode_claim_action : 2\nM140 S[bed_temperature_initial_layer_single] ;set bed temp\nM190 S[bed_temperature_initial_layer_single] ;wait for bed temp\n\n{if scan_first_layer}\n;=========register first layer scan=====\nM977 S1 P60\n{endif}\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_tool]==\"PLA\"}\n {if (bed_temperature[current_extruder] >45)||(bed_temperature_initial_layer[current_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[current_extruder] >50)||(bed_temperature_initial_layer[current_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n;===== prepare print temperature and material ==========\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp\nG91\nG0 Z10 F1200\nG90\nG28 X\nM975 S1 ; turn on \nG1 X60 F12000\nG1 Y245\nG1 Y265 F3000\nM620 M\nM620 S[initial_tool]A ; switch material if AMS exist\n M109 S[nozzle_temperature_initial_layer]\n G1 X120 F12000\n\n G1 X20 Y50 F12000\n G1 Y-3\n T[initial_tool]\n G1 X54 F12000\n G1 Y265\n M400\nM621 S[initial_tool]A\n\nM412 S1 ; ===turn on filament runout detection===\n\nM109 S250 ;set nozzle to common flush temp\nM106 P1 S0\nG92 E0\nG1 E50 F200\nM400\nM104 S[nozzle_temperature_initial_layer]\nG92 E0\nG1 E50 F200\nM400\nM106 P1 S255\nG92 E0\nG1 E5 F300\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20} ; drop nozzle temp, make filament shink a bit\nG92 E0\nG1 E-0.5 F300\n\nG1 X70 F9000\nG1 X76 F15000\nG1 X65 F15000\nG1 X76 F15000\nG1 X65 F15000; shake to put down garbage\nG1 X80 F6000\nG1 X95 F15000\nG1 X80 F15000\nG1 X165 F15000; wipe and shake\nM400\nM106 P1 S0\n;===== prepare print temperature and material end =====\n\n\n;===== wipe nozzle ===============================\nM1002 gcode_claim_action : 14\nM975 S1\nM106 S255\nG1 X65 Y230 F18000\nG1 Y264 F6000\nM109 S{nozzle_temperature_initial_layer[initial_extruder]-20}\nG1 X100 F18000 ; first wipe mouth\n\nG0 X135 Y253 F20000 ; move to exposed steel surface edge\nG28 Z P0 T300; home z with low precision,permit 300deg temperature\nG29.2 S0 ; turn off ABL\nG0 Z5 F20000\n\nG1 X60 Y265\nG92 E0\nG1 E-0.5 F300 ; retrack more\nG1 X100 F5000; second wipe mouth\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X90 F5000\nG0 X128 Y261 Z-1.5 F20000 ; move to exposed steel surface and stop the nozzle\nM104 S140 ; set temp down to heatbed acceptable\nM106 S255 ; turn on fan (G28 has turn off fan)\n\nM221 S; push soft endstop status\nM221 Z0 ;turn off Z axis endstop\nG0 Z0.5 F20000\nG0 X125 Y259.5 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 X128\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\n\nM109 S140 ; wait nozzle temp down to heatbed acceptable\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\n\nM221 R; pop softend status\nG1 Z10 F1200\nM400\nG1 Z10\nG1 F30000\nG1 X128 Y128\nG29.2 S1 ; turn on ABL\n;G28 ; home again after hard wipe mouth\nM106 S0 ; turn off fan , too noisy\n;===== wipe nozzle end ================================\n\n;===== check scanner clarity ===========================\nG1 X128 Y128 F24000\nG28 Z P0\nM972 S5 P0\nG1 X230 Y15 F24000\n;===== check scanner clarity end =======================\n\n;===== bed leveling ==================================\nM1002 judge_flag g29_before_print_flag\nM622 J1\n\n M1002 gcode_claim_action : 1\n G29 A X{first_layer_print_min[0]} Y{first_layer_print_min[1]} I{first_layer_print_size[0]} J{first_layer_print_size[1]}\n M400\n M500 ; save cali data\n\nM623\n;===== bed leveling end ================================\n\n;===== home after wipe mouth============================\nM1002 judge_flag g29_before_print_flag\nM622 J0\n\n M1002 gcode_claim_action : 13\n G28\n\nM623\n;===== home after wipe mouth end =======================\n\nM975 S1 ; turn on vibration supression\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_tool]==\"PLA\"}\n {if (bed_temperature[current_extruder] >45)||(bed_temperature_initial_layer[current_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[current_extruder] >50)||(bed_temperature_initial_layer[current_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\nM104 S{nozzle_temperature_initial_layer[initial_extruder]} ; set extrude temp earlier, to reduce wait time\n\n;===== mech mode fast check============================\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q1 A7 B30 C80 H15 K0\nM974 Q1 S2 P0\n\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q0 A7 B30 C90 Q0 H15 K0\nM974 Q0 S2 P0\n\nM975 S1\nG1 F30000\nG1 X230 Y15\nG28 X ; re-home XY \n;===== mech mode fast check============================\n\n{if scan_first_layer}\n;start heatbed scan====================================\nM976 S2 P1 \nG90\nG1 X128 Y128 F20000\nM976 S3 P2 ;register void printing detection\n{endif}\n\n;===== noozle load line ===============================\nM975 S1\nG90 \nM83\nT1000\nG1 X18.0 Y0.5 Z0.8 F18000;Move to start position\nM109 S{nozzle_temperature[initial_extruder]}\nG1 Z0.2\nG0 E2 F300\nG0 X129 E15 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \nG0 X240 E15\nG0 Y15 E1.500 F{outer_wall_volumetric_speed/(0.3*1.0)/ 4 * 60}\nG0 X239.5\nG0 E0.3\nG0 Y1.5 E1.500\nG0 X129 E15 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \nG0 X18 E15\nM400\n\n;===== for Textured PEI Plate , lower the nozzle as the nozzle was touching topmost of the texture when homing ==\n;curr_bed_type={curr_bed_type}\n{if curr_bed_type==\"Textured PEI Plate\"}\nG29.1 Z{-0.04} ; for Textured PEI Plate\n{endif}\n\n;===== draw extrinsic para cali paint =================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M1002 gcode_claim_action : 8\n\n T1000 \n G0 F3000 X28.000 Y19.500 Z0.200\n G1 F1200.0 X28.000 Y45.000 Z0.200 E1.8660 \n G1 F1200.0 X28.500 Y45.000 Z0.200 E0.0360 \n G1 F1200.0 X28.500 Y19.500 Z0.200 E1.8660 \n G1 F1200.0 X31.000 Y19.500 Z0.200 E0.1820 \n G1 F1200.0 X31.000 Y49.000 Z0.200 E2.1600 \n G1 F1200.0 X37.500 Y49.000 Z0.200 E0.4760 \n G1 F1200.0 X37.500 Y60.000 Z0.200 E0.8060 \n G1 F1200.0 X42.500 Y60.000 Z0.200 E0.3660 \n G1 F1200.0 X42.500 Y49.000 Z0.200 E0.8060 \n G1 F1200.0 X48.000 Y49.000 Z0.200 E0.4020 \n G1 F1200.0 X48.000 Y20.000 Z0.200 E2.1220 \n G1 F1200.0 X30.000 Y20.000 Z0.200 E1.3180 \n G1 F1200.0 X30.000 Y41.000 Z0.200 E1.5380 \n G1 F1200.0 X50.000 Y41.000 Z0.200 E1.4640 \n G1 F1200.0 X50.000 Y34.000 Z0.200 E0.5120 \n G1 F1200.0 X30.000 Y34.000 Z0.200 E1.4640 \n G1 F1500.000 E-0.800 \n\n ;=========== extruder cali extrusion ================== \n T1000 \n M83\n {if default_acceleration > 0}\n {if outer_wall_acceleration > 0}\n M204 S[outer_wall_acceleration]\n {else}\n M204 S[default_acceleration]\n {endif}\n {endif}\n G0 X35.000 Y18.000 Z0.300 F30000 E0\n G1 F1500.000 E0.800 \n M106 S0 ; turn off fan\n G0 X110.000 E9.35441 F4800 \n G0 X185.000 E9.35441 F4800 \n G0 X187 Z0\n G1 F1500.000 E-0.800 \n G0 Z1\n G0 X180 Z0.3 F18000\n \n M900 L1000.0 M1.0\n M900 K0.020 \n G0 X45.000 F30000 \n G0 Y20.000 F30000 \n G1 F1500.000 E0.800 \n G1 X65.000 E2.4945 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X70.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X75.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X80.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X85.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X90.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X95.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X100.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X105.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X110.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X115.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X120.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X125.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X130.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X135.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X140.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X145.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X150.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X155.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X160.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X165.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X170.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X175.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X180.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 F1500.000 E-0.800 \n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos \n G1 Z0.3\n M400\n\n G0 X45.000 F30000 \n M900 K0.010 \n G0 X45.000 F30000 \n G0 Y22.000 F30000 \n G1 F1500.000 E0.800 \n G1 X65.000 E2.4945 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X70.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X75.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X80.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X85.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X90.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X95.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X100.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X105.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X110.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X115.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X120.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X125.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X130.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X135.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X140.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X145.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X150.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X155.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X160.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X165.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X170.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X175.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X180.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 F1500.000 E-0.800 \n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos \n G1 Z0.3\n M400\n\n G0 X45.000 F30000 \n M900 K0.000 \n G0 X45.000 F30000 \n G0 Y24.000 F30000 \n G1 F1500.000 E0.800 \n G1 X65.000 E2.4945 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X70.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X75.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X80.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X85.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X90.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X95.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X100.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X105.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X110.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X115.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X120.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X125.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X130.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X135.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X140.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X145.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X150.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X155.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X160.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X165.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X170.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X175.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X180.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 F1500.000 E-0.800\n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos \n G1 Z0.3\n\n G0 X45.000 F30000 ; move to start point\n\nM623 ; end of \"draw extrinsic para cali paint\"\n\nM104 S140\n\n\n;=========== laser and rgb calibration =========== \nM400\nM18 E\nM500 R\n\nM973 S3 P14\n\nG1 X120 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\nT1100\nG1 X143.0 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\n\nM400 P100\n\nM960 S1 P1\nM400 P100\nM973 S6 P0; use auto exposure for horizontal laser by xcam\nM960 S0 P0\n\nG1 X240.0 Y6.0 Z0.3 F18000.0;Move to vertical extrude line pos\nM960 S2 P1\nM400 P100\nM973 S6 P1; use auto exposure for vertical laser by xcam\nM960 S0 P0\n\n;=========== handeye calibration ======================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M973 S3 P1 ; camera start stream\n M400 P500\n M973 S1 \n G0 F6000 X40.000 Y54.500 Z0.000 \n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500 \n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P1 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P200 \n M971 S5 P3 \n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P2 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P500 \n M971 S5 P4 \n M963 S1 \n M400 P1500 \n M964 \n T1100 \n G0 F6000 X40.000 Y54.500 Z0.000 \n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500 \n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P1 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P200 \n M971 S5 P3 \n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1 \n G0 Y37.50 \n M400 P200\n M971 S5 P2 \n M960 S0 P0\n M960 S2 P1 \n G0 Y54.50 \n M400 P500 \n M971 S5 P4 \n M963 S1 \n M400 P1500 \n M964 \n T1100 \n G1 Z3 F3000 \n\n M400\n M500 ; save cali data\n\n M104 S{nozzle_temperature_initial_layer[initial_extruder]} ; rise nozzle temp now ,to reduce temp waiting time.\n\n T1100 \n M400 P400 \n M960 S0 P0\n G0 F30000.000 Y22.000 X65.000 Z0.000\n M400 P400 \n M960 S1 P1 \n M400 P50 \n\n M969 S1 N3 A2000 \n G0 F360.000 X181.000 Z0.000\n M980.3 A70.000 B{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60/4} C5.000 D{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60} E5.000 F175.000 H1.000 I0.000 J0.010 K0.020\n M400 P100 \n G0 F20000\n G0 Z1 ; rise nozzle up\n T1000 ; change to nozzle space\n G0 X45.000 Y16.000 F30000 ; move to test line pos\n M969 S0 ; turn off scanning\n M960 S0 P0\n\n\n G1 Z2 F20000 \n T1000 \n G0 X45.000 Y16.000 F30000 E0\n M109 S{nozzle_temperature_initial_layer[initial_extruder]} \n G0 Z0.3\n G1 F1500.000 E3.600 \n G1 X65.000 E2.4945 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X70.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X75.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X80.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X85.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X90.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X95.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X100.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X105.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X110.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X115.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X120.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X125.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X130.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X135.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n\n ; see if extrude cali success, if not ,use default value\n M1002 judge_last_extrude_cali_success\n M622 J0\n M400\n M900 K0.01 M{outer_wall_volumetric_speed/(1.75*1.75/4*3.14) *0.01}\n M623 \n\n G1 X140.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X145.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X150.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X155.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X160.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X165.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X170.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X175.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X180.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X185.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X190.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X195.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X200.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X205.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X210.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X215.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n G1 X220.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60} \n G1 X225.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60} \n M973 S4 \n\nM623\n\n;========turn off light and wait extrude temperature =============\nM1002 gcode_claim_action : 0\nM973 S4 ; turn off scanner\nM400 ; wait all motion done before implement the emprical L parameters\n;M900 L500.0 ; Empirical parameters\nM109 S[nozzle_temperature_initial_layer]\nM960 S1 P0 ; turn off laser\nM960 S2 P0 ; turn off laser\nM106 S0 ; turn off fan\nM106 P2 S0 ; turn off big fan \nM106 P3 S0 ; turn off chamber fan\n\nM975 S1 ; turn on mech mode supression\nG90 \nM83\nT1000\nG1 E{-retraction_length[initial_extruder]} F1800\nG1 X128.0 Y253.0 Z0.2 F24000.0;Move to start position\nG1 E{retraction_length[initial_extruder]} F1800\nM109 S{nozzle_temperature_initial_layer[initial_extruder]}\nG0 X253 E6.4 F{outer_wall_volumetric_speed/(0.3*0.6) * 60} \nG0 Y128 E6.4\nG0 X252.5\nG0 Y252.5 E6.4\nG0 X128 E6.4", + "machine_start_gcode": ";===== machine: X1 =========================\n;===== date: 20230322 =====================\n;===== turn on the HB fan =================\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp to turn on the HB fan\n;===== reset machine status =================\nG91\nM17 Z0.4 ; lower the z-motor current\nG0 Z12 F300 ; lower the hotbed , to prevent the nozzle is below the hotbed\nG0 Z-6;\nG90\nM17 X1.2 Y1.2 Z0.75 ; reset motor current to default\nM960 S5 P1 ; turn on logo lamp\nG90\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\nM73.2 R1.0 ;Reset left time magnitude\nM1002 set_gcode_claim_speed_level : 5\nM221 X0 Y0 Z0 ; turn off soft endstop to prevent protential logic problem\nG29.1 Z{0.0} ; clear z-trim value first\n\n;===== heatbed preheat ====================\nM1002 gcode_claim_action : 2\nM140 S[bed_temperature_initial_layer_single] ;set bed temp\nM190 S[bed_temperature_initial_layer_single] ;wait for bed temp\n\n{if scan_first_layer}\n;=========register first layer scan=====\nM977 S1 P60\n{endif}\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_no_support_extruder]==\"PLA\"}\n {if (bed_temperature[initial_no_support_extruder] >45)||(bed_temperature_initial_layer[initial_no_support_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[initial_no_support_extruder] >50)||(bed_temperature_initial_layer[initial_no_support_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\n;===== prepare print temperature and material ==========\nM104 S[nozzle_temperature_initial_layer] ;set extruder temp\nG91\nG0 Z10 F1200\nG90\nG28 X\nM975 S1 ; turn on\nG1 X60 F12000\nG1 Y245\nG1 Y265 F3000\nM620 M\nM620 S[initial_no_support_extruder]A ; switch material if AMS exist\n M109 S[nozzle_temperature_initial_layer]\n G1 X120 F12000\n\n G1 X20 Y50 F12000\n G1 Y-3\n T[initial_no_support_extruder]\n G1 X54 F12000\n G1 Y265\n M400\nM621 S[initial_no_support_extruder]A\nM620.1 E F{filament_max_volumetric_speed[initial_no_support_extruder]/2.4053*60} T{nozzle_temperature_range_high[initial_no_support_extruder]}\n\n\nM412 S1 ; ===turn on filament runout detection===\n\nM109 S250 ;set nozzle to common flush temp\nM106 P1 S0\nG92 E0\nG1 E50 F200\nM400\nM104 S[nozzle_temperature_initial_layer]\nG92 E0\nG1 E50 F200\nM400\nM106 P1 S255\nG92 E0\nG1 E5 F300\nM109 S{nozzle_temperature_initial_layer[initial_no_support_extruder]-20} ; drop nozzle temp, make filament shink a bit\nG92 E0\nG1 E-0.5 F300\n\nG1 X70 F9000\nG1 X76 F15000\nG1 X65 F15000\nG1 X76 F15000\nG1 X65 F15000; shake to put down garbage\nG1 X80 F6000\nG1 X95 F15000\nG1 X80 F15000\nG1 X165 F15000; wipe and shake\nM400\nM106 P1 S0\n;===== prepare print temperature and material end =====\n\n\n;===== wipe nozzle ===============================\nM1002 gcode_claim_action : 14\nM975 S1\nM106 S255\nG1 X65 Y230 F18000\nG1 Y264 F6000\nM109 S{nozzle_temperature_initial_layer[initial_no_support_extruder]-20}\nG1 X100 F18000 ; first wipe mouth\n\nG0 X135 Y253 F20000 ; move to exposed steel surface edge\nG28 Z P0 T300; home z with low precision,permit 300deg temperature\nG29.2 S0 ; turn off ABL\nG0 Z5 F20000\n\nG1 X60 Y265\nG92 E0\nG1 E-0.5 F300 ; retrack more\nG1 X100 F5000; second wipe mouth\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X100 F5000\nG1 X70 F15000\nG1 X90 F5000\nG0 X128 Y261 Z-1.5 F20000 ; move to exposed steel surface and stop the nozzle\nM104 S140 ; set temp down to heatbed acceptable\nM106 S255 ; turn on fan (G28 has turn off fan)\n\nM221 S; push soft endstop status\nM221 Z0 ;turn off Z axis endstop\nG0 Z0.5 F20000\nG0 X125 Y259.5 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y262.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y260.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.5\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 Z0.5 F20000\nG0 X125 Y261.0\nG0 Z-1.01\nG0 X131 F211\nG0 X124\nG0 X128\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\nG2 I0.5 J0 F300\n\nM109 S140 ; wait nozzle temp down to heatbed acceptable\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\nG2 I0.5 J0 F3000\n\nM221 R; pop softend status\nG1 Z10 F1200\nM400\nG1 Z10\nG1 F30000\nG1 X128 Y128\nG29.2 S1 ; turn on ABL\n;G28 ; home again after hard wipe mouth\nM106 S0 ; turn off fan , too noisy\n;===== wipe nozzle end ================================\n\n;===== check scanner clarity ===========================\nG1 X128 Y128 F24000\nG28 Z P0\nM972 S5 P0\nG1 X230 Y15 F24000\n;===== check scanner clarity end =======================\n\n;===== bed leveling ==================================\nM1002 judge_flag g29_before_print_flag\nM622 J1\n\n M1002 gcode_claim_action : 1\n G29 A X{first_layer_print_min[0]} Y{first_layer_print_min[1]} I{first_layer_print_size[0]} J{first_layer_print_size[1]}\n M400\n M500 ; save cali data\n\nM623\n;===== bed leveling end ================================\n\n;===== home after wipe mouth============================\nM1002 judge_flag g29_before_print_flag\nM622 J0\n\n M1002 gcode_claim_action : 13\n G28\n\nM623\n;===== home after wipe mouth end =======================\n\nM975 S1 ; turn on vibration supression\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_no_support_extruder]==\"PLA\"}\n {if (bed_temperature[initial_no_support_extruder] >45)||(bed_temperature_initial_layer[initial_no_support_extruder] >45)}\n M106 P3 S180\n {elsif (bed_temperature[initial_no_support_extruder] >50)||(bed_temperature_initial_layer[initial_no_support_extruder] >50)}\n M106 P3 S255\n {endif};Prevent PLA from jamming\n{endif}\nM106 P2 S100 ; turn on big fan ,to cool down toolhead\n\nM104 S{nozzle_temperature_initial_layer[initial_no_support_extruder]} ; set extrude temp earlier, to reduce wait time\n\n;===== mech mode fast check============================\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q1 A7 B30 C80 H15 K0\nM974 Q1 S2 P0\n\nG1 X128 Y128 Z10 F20000\nM400 P200\nM970.3 Q0 A7 B30 C90 Q0 H15 K0\nM974 Q0 S2 P0\n\nM975 S1\nG1 F30000\nG1 X230 Y15\nG28 X ; re-home XY\n;===== mech mode fast check============================\n\n{if scan_first_layer}\n;start heatbed scan====================================\nM976 S2 P1\nG90\nG1 X128 Y128 F20000\nM976 S3 P2 ;register void printing detection\n{endif}\n\n;===== noozle load line ===============================\nM975 S1\nG90\nM83\nT1000\nG1 X18.0 Y0.5 Z0.8 F18000;Move to start position\nM109 S{nozzle_temperature[initial_no_support_extruder]}\nG1 Z0.2\nG0 E2 F300\nG0 X129 E15 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\nG0 X240 E15\nG0 Y15 E1.500 F{outer_wall_volumetric_speed/(0.3*1.0)/ 4 * 60}\nG0 X239.5\nG0 E0.3\nG0 Y1.5 E1.500\nG0 X129 E15 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\nG0 X18 E15\nM400\n\n;===== for Textured PEI Plate , lower the nozzle as the nozzle was touching topmost of the texture when homing ==\n;curr_bed_type={curr_bed_type}\n{if curr_bed_type==\"Textured PEI Plate\"}\nG29.1 Z{-0.04} ; for Textured PEI Plate\n{endif}\n\n;===== draw extrinsic para cali paint =================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M1002 gcode_claim_action : 8\n\n T1000\n G0 F3000 X28.000 Y19.500 Z0.200\n G1 F1200.0 X28.000 Y45.000 Z0.200 E1.8660\n G1 F1200.0 X28.500 Y45.000 Z0.200 E0.0360\n G1 F1200.0 X28.500 Y19.500 Z0.200 E1.8660\n G1 F1200.0 X31.000 Y19.500 Z0.200 E0.1820\n G1 F1200.0 X31.000 Y49.000 Z0.200 E2.1600\n G1 F1200.0 X37.500 Y49.000 Z0.200 E0.4760\n G1 F1200.0 X37.500 Y60.000 Z0.200 E0.8060\n G1 F1200.0 X42.500 Y60.000 Z0.200 E0.3660\n G1 F1200.0 X42.500 Y49.000 Z0.200 E0.8060\n G1 F1200.0 X48.000 Y49.000 Z0.200 E0.4020\n G1 F1200.0 X48.000 Y20.000 Z0.200 E2.1220\n G1 F1200.0 X30.000 Y20.000 Z0.200 E1.3180\n G1 F1200.0 X30.000 Y41.000 Z0.200 E1.5380\n G1 F1200.0 X50.000 Y41.000 Z0.200 E1.4640\n G1 F1200.0 X50.000 Y34.000 Z0.200 E0.5120\n G1 F1200.0 X30.000 Y34.000 Z0.200 E1.4640\n G1 F1500.000 E-0.800\n\n ;=========== extruder cali extrusion ==================\n T1000\n M83\n {if default_acceleration > 0}\n {if outer_wall_acceleration > 0}\n M204 S[outer_wall_acceleration]\n {else}\n M204 S[default_acceleration]\n {endif}\n {endif}\n G0 X35.000 Y18.000 Z0.300 F30000 E0\n G1 F1500.000 E0.800\n M106 S0 ; turn off fan\n G0 X110.000 E9.35441 F4800\n G0 X185.000 E9.35441 F4800\n G0 X187 Z0\n G1 F1500.000 E-0.800\n G0 Z1\n G0 X180 Z0.3 F18000\n\n M900 L1000.0 M1.0\n M900 K0.020\n G0 X45.000 F30000\n G0 Y20.000 F30000\n G1 F1500.000 E0.800\n G1 X65.000 E2.4945 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X70.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X75.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X80.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X85.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X90.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X95.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X100.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X105.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X110.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X115.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X120.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X125.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X130.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X135.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X140.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X145.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X150.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X155.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X160.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X165.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X170.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X175.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X180.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 F1500.000 E-0.800\n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos\n G1 Z0.3\n M400\n\n G0 X45.000 F30000\n M900 K0.010\n G0 X45.000 F30000\n G0 Y22.000 F30000\n G1 F1500.000 E0.800\n G1 X65.000 E2.4945 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X70.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X75.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X80.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X85.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X90.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X95.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X100.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X105.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X110.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X115.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X120.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X125.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X130.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X135.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X140.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X145.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X150.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X155.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X160.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X165.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X170.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X175.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X180.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 F1500.000 E-0.800\n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos\n G1 Z0.3\n M400\n\n G0 X45.000 F30000\n M900 K0.000\n G0 X45.000 F30000\n G0 Y24.000 F30000\n G1 F1500.000 E0.800\n G1 X65.000 E2.4945 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X70.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X75.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X80.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X85.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X90.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X95.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X100.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X105.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X110.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X115.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X120.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X125.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X130.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X135.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X140.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X145.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X150.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X155.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X160.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X165.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X170.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X175.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X180.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 F1500.000 E-0.800\n G1 X183 Z0.15 F30000\n G1 X185\n G1 Z1.0\n G0 Y18.000 F30000 ; move y to clear pos\n G1 Z0.3\n\n G0 X45.000 F30000 ; move to start point\n\nM623 ; end of \"draw extrinsic para cali paint\"\n\nM104 S140\n\n\n;=========== laser and rgb calibration ===========\nM400\nM18 E\nM500 R\n\nM973 S3 P14\n\nG1 X120 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\nT1100\nG1 X235.0 Y1.0 Z0.3 F18000.0;Move to first extrude line pos\nM400 P100\nM960 S1 P1\nM400 P100\nM973 S6 P0; use auto exposure for horizontal laser by xcam\nM960 S0 P0\n\nG1 X240.0 Y6.0 Z0.3 F18000.0;Move to vertical extrude line pos\nM960 S2 P1\nM400 P100\nM973 S6 P1; use auto exposure for vertical laser by xcam\nM960 S0 P0\n\n;=========== handeye calibration ======================\nM1002 judge_flag extrude_cali_flag\nM622 J1\n\n M973 S3 P1 ; camera start stream\n M400 P500\n M973 S1\n G0 F6000 X40.000 Y54.500 Z0.000\n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500\n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1\n G0 Y37.50\n M400 P200\n M971 S5 P1\n M960 S0 P0\n M960 S2 P1\n G0 Y54.50\n M400 P200\n M971 S5 P3\n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1\n G0 Y37.50\n M400 P200\n M971 S5 P2\n M960 S0 P0\n M960 S2 P1\n G0 Y54.50\n M400 P500\n M971 S5 P4\n M963 S1\n M400 P1500\n M964\n T1100\n G0 F6000 X40.000 Y54.500 Z0.000\n M960 S0 P1\n M973 S1\n M400 P800\n M971 S6 P0\n M973 S2 P16000\n M400 P500\n G0 Z0.000 F12000\n M960 S0 P0\n M960 S1 P1\n G0 Y37.50\n M400 P200\n M971 S5 P1\n M960 S0 P0\n M960 S2 P1\n G0 Y54.50\n M400 P200\n M971 S5 P3\n G0 Z0.500 F12000\n M960 S0 P0\n M960 S1 P1\n G0 Y37.50\n M400 P200\n M971 S5 P2\n M960 S0 P0\n M960 S2 P1\n G0 Y54.50\n M400 P500\n M971 S5 P4\n M963 S1\n M400 P1500\n M964\n T1100\n G1 Z3 F3000\n\n M400\n M500 ; save cali data\n\n M104 S{nozzle_temperature_initial_layer[initial_no_support_extruder]} ; rise nozzle temp now ,to reduce temp waiting time.\n\n T1100\n M400 P400\n M960 S0 P0\n G0 F30000.000 Y22.000 X65.000 Z0.000\n M400 P400\n M960 S1 P1\n M400 P50\n\n M969 S1 N3 A2000\n G0 F360.000 X181.000 Z0.000\n M980.3 A70.000 B{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60/4} C5.000 D{outer_wall_volumetric_speed/(1.75*1.75/4*3.14)*60} E5.000 F175.000 H1.000 I0.000 J0.010 K0.020\n M400 P100\n G0 F20000\n G0 Z1 ; rise nozzle up\n T1000 ; change to nozzle space\n G0 X45.000 Y16.000 F30000 ; move to test line pos\n M969 S0 ; turn off scanning\n M960 S0 P0\n\n\n G1 Z2 F20000\n T1000\n G0 X45.000 Y16.000 F30000 E0\n M109 S{nozzle_temperature_initial_layer[initial_no_support_extruder]}\n G0 Z0.3\n G1 F1500.000 E3.600\n G1 X65.000 E2.4945 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X70.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X75.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X80.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X85.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X90.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X95.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X100.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X105.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X110.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X115.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X120.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X125.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X130.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X135.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n\n ; see if extrude cali success, if not ,use default value\n M1002 judge_last_extrude_cali_success\n M622 J0\n M400\n M900 K0.01 M{outer_wall_volumetric_speed/(1.75*1.75/4*3.14) *0.01}\n M623\n\n G1 X140.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X145.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X150.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X155.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X160.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X165.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X170.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X175.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X180.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X185.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X190.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X195.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X200.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X205.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X210.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X215.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n G1 X220.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) / 4 * 60}\n G1 X225.000 E0.6236 F{outer_wall_volumetric_speed/(0.3*1.0) * 60}\n M973 S4\n\nM623\n\n;========turn off light and wait extrude temperature =============\nM1002 gcode_claim_action : 0\nM973 S4 ; turn off scanner\nM400 ; wait all motion done before implement the emprical L parameters\n;M900 L500.0 ; Empirical parameters\nM109 S[nozzle_temperature_initial_layer]\nM960 S1 P0 ; turn off laser\nM960 S2 P0 ; turn off laser\nM106 S0 ; turn off fan\nM106 P2 S0 ; turn off big fan\nM106 P3 S0 ; turn off chamber fan\n\nM975 S1 ; turn on mech mode supression\nG90\nM83\nT1000\nG1 E{-retraction_length[initial_no_support_extruder]} F1800\nG1 X128.0 Y253.0 Z0.2 F24000.0;Move to start position\nG1 E{retraction_length[initial_no_support_extruder]} F1800\nM109 S{nozzle_temperature_initial_layer[initial_no_support_extruder]}\nG0 X253 E6.4 F{outer_wall_volumetric_speed/(0.3*0.6) * 60}\nG0 Y128 E6.4\nG0 X252.5\nG0 Y252.5 E6.4\nG0 X128 E6.4", "upward_compatible_machine":[ "Bambu Lab P1P 0.8 nozzle", "Bambu Lab X1 0.8 nozzle" diff --git a/resources/profiles/BBL/machine/fdm_bbl_3dp_001_common.json b/resources/profiles/BBL/machine/fdm_bbl_3dp_001_common.json index dcf62d84b7..3137c9b501 100644 --- a/resources/profiles/BBL/machine/fdm_bbl_3dp_001_common.json +++ b/resources/profiles/BBL/machine/fdm_bbl_3dp_001_common.json @@ -156,10 +156,10 @@ "nozzle_type": "hardened_steel", "silent_mode": "0", "single_extruder_multi_material": "1", - "change_filament_gcode": "M620 S[next_extruder]A\nM204 S9000\n{if toolchange_count > 1 && (z_hop_types[current_extruder] == 0 || z_hop_types[current_extruder] == 3)}\nG17\nG2 Z{z_after_toolchange + 0.4} I0.86 J0.86 P1 F10000 ; spiral lift a little from second lift\n{endif}\nG1 Z{max_layer_z + 3.0} F1200\n\nG1 X70 F21000\nG1 Y245\nG1 Y265 F3000\nM400\nM106 P1 S0\nM106 P2 S0\n{if old_filament_temp > 142 && next_extruder < 255}M104 S[old_filament_temp]{endif}\nG1 X90 F3000\nG1 Y255 F4000\nG1 X100 F5000\nG1 X120 F15000\n\nG1 X20 Y50 F21000\nG1 Y-3\n{if toolchange_count == 2}\n; get travel path for change filament\nM620.1 X[travel_point_1_x] Y[travel_point_1_y] F21000 P0\nM620.1 X[travel_point_2_x] Y[travel_point_2_y] F21000 P1\nM620.1 X[travel_point_3_x] Y[travel_point_3_y] F21000 P2\n{endif}\nT[next_extruder]\nM620.1 E F{new_filament_e_feedrate}\n\n{if next_extruder < 255}\nM400\n\nG92 E0\n{if flush_length_1 > 1}\n; FLUSH_START\n; always use highest temperature to flush\nM400\nM109 S[nozzle_temperature_range_high]\n{if flush_length_1 > 23.7}\nG1 E23.7 F{old_filament_e_feedrate} ; do not need pulsatile flushing for start part\nG1 E{(flush_length_1 - 23.7) * 0.02} F50\nG1 E{(flush_length_1 - 23.7) * 0.23} F{old_filament_e_feedrate}\nG1 E{(flush_length_1 - 23.7) * 0.02} F50\nG1 E{(flush_length_1 - 23.7) * 0.23} F{new_filament_e_feedrate}\nG1 E{(flush_length_1 - 23.7) * 0.02} F50\nG1 E{(flush_length_1 - 23.7) * 0.23} F{new_filament_e_feedrate}\nG1 E{(flush_length_1 - 23.7) * 0.02} F50\nG1 E{(flush_length_1 - 23.7) * 0.23} F{new_filament_e_feedrate}\n{else}\nG1 E{flush_length_1} F{old_filament_e_feedrate}\n{endif}\n; FLUSH_END\nG1 E-[old_retract_length_toolchange] F1800\nG1 E[old_retract_length_toolchange] F300\n{endif}\n\n{if flush_length_2 > 1}\n; FLUSH_START\nG1 E{flush_length_2 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_2 * 0.02} F50\nG1 E{flush_length_2 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_2 * 0.02} F50\nG1 E{flush_length_2 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_2 * 0.02} F50\nG1 E{flush_length_2 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_2 * 0.02} F50\nG1 E{flush_length_2 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_2 * 0.02} F50\n; FLUSH_END\nG1 E-[new_retract_length_toolchange] F1800\nG1 E[new_retract_length_toolchange] F300\n{endif}\n\n{if flush_length_3 > 1}\n; FLUSH_START\nG1 E{flush_length_3 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_3 * 0.02} F50\nG1 E{flush_length_3 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_3 * 0.02} F50\nG1 E{flush_length_3 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_3 * 0.02} F50\nG1 E{flush_length_3 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_3 * 0.02} F50\nG1 E{flush_length_3 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_3 * 0.02} F50\n; FLUSH_END\nG1 E-[new_retract_length_toolchange] F1800\nG1 E[new_retract_length_toolchange] F300\n{endif}\n\n{if flush_length_4 > 1}\n; FLUSH_START\nG1 E{flush_length_4 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_4 * 0.02} F50\nG1 E{flush_length_4 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_4 * 0.02} F50\nG1 E{flush_length_4 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_4 * 0.02} F50\nG1 E{flush_length_4 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_4 * 0.02} F50\nG1 E{flush_length_4 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_4 * 0.02} F50\n; FLUSH_END\n{endif}\n; FLUSH_START\nM400\nM109 S[new_filament_temp]\nG1 E2 F{new_filament_e_feedrate} ;Compensate for filament spillage during waiting temperature\n; FLUSH_END\nM400\nG92 E0\nG1 E-[new_retract_length_toolchange] F1800\nM106 P1 S255\nM400 S3\nG1 X80 F15000\nG1 X60 F15000\nG1 X80 F15000\nG1 X60 F15000; shake to put down garbage\n\nG1 X70 F5000\nG1 X90 F3000\nG1 Y255 F4000\nG1 X100 F5000\nG1 Y265 F5000\nG1 X70 F10000\nG1 X100 F5000\nG1 X70 F10000\nG1 X100 F5000\nG1 X165 F15000; wipe and shake\nG1 Y256 ; move Y to aside, prevent collision\nM400\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", + "change_filament_gcode": "M620 S[next_extruder]A\nM204 S9000\n{if toolchange_count > 1 && (z_hop_types[current_extruder] == 0 || z_hop_types[current_extruder] == 3)}\nG17\nG2 Z{z_after_toolchange + 0.4} I0.86 J0.86 P1 F10000 ; spiral lift a little from second lift\n{endif}\nG1 Z{max_layer_z + 3.0} F1200\n\nG1 X70 F21000\nG1 Y245\nG1 Y265 F3000\nM400\nM106 P1 S0\nM106 P2 S0\n{if old_filament_temp > 142 && next_extruder < 255}M104 S[old_filament_temp]{endif}\nG1 X90 F3000\nG1 Y255 F4000\nG1 X100 F5000\nG1 X120 F15000\n\nG1 X20 Y50 F21000\nG1 Y-3\n{if toolchange_count == 2}\n; get travel path for change filament\nM620.1 X[travel_point_1_x] Y[travel_point_1_y] F21000 P0\nM620.1 X[travel_point_2_x] Y[travel_point_2_y] F21000 P1\nM620.1 X[travel_point_3_x] Y[travel_point_3_y] F21000 P2\n{endif}\nM620.1 E F[old_filament_e_feedrate] T{nozzle_temperature_range_high[previous_extruder]}\nT[next_extruder]\nM620.1 E F[new_filament_e_feedrate] T{nozzle_temperature_range_high[next_extruder]}\n\n{if next_extruder < 255}\nM400\n\nG92 E0\n{if flush_length_1 > 1}\n; FLUSH_START\n; always use highest temperature to flush\nM400\nM109 S[nozzle_temperature_range_high]\n{if flush_length_1 > 23.7}\nG1 E23.7 F{old_filament_e_feedrate} ; do not need pulsatile flushing for start part\nG1 E{(flush_length_1 - 23.7) * 0.02} F50\nG1 E{(flush_length_1 - 23.7) * 0.23} F{old_filament_e_feedrate}\nG1 E{(flush_length_1 - 23.7) * 0.02} F50\nG1 E{(flush_length_1 - 23.7) * 0.23} F{new_filament_e_feedrate}\nG1 E{(flush_length_1 - 23.7) * 0.02} F50\nG1 E{(flush_length_1 - 23.7) * 0.23} F{new_filament_e_feedrate}\nG1 E{(flush_length_1 - 23.7) * 0.02} F50\nG1 E{(flush_length_1 - 23.7) * 0.23} F{new_filament_e_feedrate}\n{else}\nG1 E{flush_length_1} F{old_filament_e_feedrate}\n{endif}\n; FLUSH_END\nG1 E-[old_retract_length_toolchange] F1800\nG1 E[old_retract_length_toolchange] F300\n{endif}\n\n{if flush_length_2 > 1}\n; FLUSH_START\nG1 E{flush_length_2 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_2 * 0.02} F50\nG1 E{flush_length_2 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_2 * 0.02} F50\nG1 E{flush_length_2 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_2 * 0.02} F50\nG1 E{flush_length_2 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_2 * 0.02} F50\nG1 E{flush_length_2 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_2 * 0.02} F50\n; FLUSH_END\nG1 E-[new_retract_length_toolchange] F1800\nG1 E[new_retract_length_toolchange] F300\n{endif}\n\n{if flush_length_3 > 1}\n; FLUSH_START\nG1 E{flush_length_3 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_3 * 0.02} F50\nG1 E{flush_length_3 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_3 * 0.02} F50\nG1 E{flush_length_3 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_3 * 0.02} F50\nG1 E{flush_length_3 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_3 * 0.02} F50\nG1 E{flush_length_3 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_3 * 0.02} F50\n; FLUSH_END\nG1 E-[new_retract_length_toolchange] F1800\nG1 E[new_retract_length_toolchange] F300\n{endif}\n\n{if flush_length_4 > 1}\n; FLUSH_START\nG1 E{flush_length_4 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_4 * 0.02} F50\nG1 E{flush_length_4 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_4 * 0.02} F50\nG1 E{flush_length_4 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_4 * 0.02} F50\nG1 E{flush_length_4 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_4 * 0.02} F50\nG1 E{flush_length_4 * 0.18} F{new_filament_e_feedrate}\nG1 E{flush_length_4 * 0.02} F50\n; FLUSH_END\n{endif}\n; FLUSH_START\nM400\nM109 S[new_filament_temp]\nG1 E2 F{new_filament_e_feedrate} ;Compensate for filament spillage during waiting temperature\n; FLUSH_END\nM400\nG92 E0\nG1 E-[new_retract_length_toolchange] F1800\nM106 P1 S255\nM400 S3\nG1 X80 F15000\nG1 X60 F15000\nG1 X80 F15000\nG1 X60 F15000; shake to put down garbage\n\nG1 X70 F5000\nG1 X90 F3000\nG1 Y255 F4000\nG1 X100 F5000\nG1 Y265 F5000\nG1 X70 F10000\nG1 X100 F5000\nG1 X70 F10000\nG1 X100 F5000\nG1 X165 F15000; wipe and shake\nG1 Y256 ; move Y to aside, prevent collision\nM400\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", "layer_change_gcode": "; layer num/total_layer_count: {layer_num+1}/[total_layer_count]\nM622.1 S1 ; for prev firware, default turned on\nM1002 judge_flag timelapse_record_flag\nM622 J1\n{if timelapse_type == 0} ; timelapse without wipe tower\nM971 S11 C10 O0\n{elsif timelapse_type == 1} ; timelapse with wipe tower\nG92 E0\nG1 E-[retraction_length] F1800\nG17\nG2 Z{layer_z + 0.4} I0.86 J0.86 P1 F20000 ; spiral lift a little\nG1 X65 Y245 F20000 ; move to safe pos\nG17\nG2 Z{layer_z} I0.86 J0.86 P1 F20000\nG1 Y265 F3000\nM400 P300\nM971 S11 C10 O0\nG92 E0\nG1 E[retraction_length] F300\nG1 X100 F5000\nG1 Y255 F20000\n{endif}\nM623\n; update layer progress\nM73 L{layer_num+1}\nM991 S0 P{layer_num} ;notify layer change", "machine_end_gcode": ";===== date: 20230118 =====================\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\nG1 X65 Y245 F12000 ; move to safe pos \nG1 Y265 F3000\nM991 S0 P-1 ;end smooth timelapse at safe pos\nM400 S2 ;wait for last picture to be taken\n\nG1 X65 Y245 F12000\nG1 Y265 F3000\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\nG1 X100 F12000 ; wipe\n; pull back filament to AMS\nM620 S255\nG1 X20 Y50 F12000\nG1 Y-3\nT255\nG1 X65 F12000\nG1 Y265\nG1 X100 F12000 ; wipe\nM621 S255\nM104 S0 ; 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) < 250}\n G1 Z{max_layer_z + 100.0} F600\n G1 Z{max_layer_z +98.0}\n{else}\n G1 Z250 F600\n G1 Z248\n{endif}\nM400 P100\nM17 R ; restore z current\n\nG90\nG1 X128 Y250 F3600\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\nM17 X0.8 Y0.8 Z0.5 ; lower motor current to 45% power\n\n", - "machine_pause_gcode": "M400 U1\n", + "machine_pause_gcode": "M400 U1", "wipe": [ "1" ] diff --git a/resources/profiles/Voron/machine/fdm_klipper_common.json b/resources/profiles/Voron/machine/fdm_klipper_common.json index c2e708c31a..f74a4fbce5 100644 --- a/resources/profiles/Voron/machine/fdm_klipper_common.json +++ b/resources/profiles/Voron/machine/fdm_klipper_common.json @@ -120,7 +120,7 @@ "silent_mode": "0", "single_extruder_multi_material": "1", "change_filament_gcode": "", - "machine_pause_gcode": "PAUSE\n", + "machine_pause_gcode": "PAUSE", "wipe": [ "1" ], diff --git a/resources/shaders/thumbnail.fs b/resources/shaders/thumbnail.fs new file mode 100644 index 0000000000..4b269734ee --- /dev/null +++ b/resources/shaders/thumbnail.fs @@ -0,0 +1,16 @@ +#version 110 + +uniform vec4 uniform_color; +uniform float emission_factor; + +// x = tainted, y = specular; +varying vec2 intensity; +//varying float drop; +varying vec4 world_pos; + +void main() +{ + if (world_pos.z < 0.0) + discard; + gl_FragColor = vec4(vec3(intensity.y) + uniform_color.rgb * (intensity.x + emission_factor), uniform_color.a); +} diff --git a/resources/shaders/thumbnail.vs b/resources/shaders/thumbnail.vs new file mode 100644 index 0000000000..a02248f28d --- /dev/null +++ b/resources/shaders/thumbnail.vs @@ -0,0 +1,43 @@ +#version 110 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +#define INTENSITY_AMBIENT 0.3 + +uniform mat4 volume_world_matrix; +// x = tainted, y = specular; +varying vec2 intensity; +varying vec4 world_pos; + +void main() +{ + // First transform the normal into camera space and normalize the result. + vec3 normal = normalize(gl_NormalMatrix * gl_Normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec3 position = (gl_ModelViewMatrix * gl_Vertex).xyz; + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + // Point in homogenous coordinates. + world_pos = volume_world_matrix * gl_Vertex; + + gl_Position = ftransform(); +} diff --git a/resources/tooltip/privacyupdate.html b/resources/tooltip/privacyupdate.html index 3e7c22c174..c67ccf83f0 100644 --- a/resources/tooltip/privacyupdate.html +++ b/resources/tooltip/privacyupdate.html @@ -6,7 +6,7 @@ - +
diff --git a/resources/web/homepage/js/globalapi.js b/resources/web/homepage/js/globalapi.js index ab7d5e6a20..80bbaa01d5 100644 --- a/resources/web/homepage/js/globalapi.js +++ b/resources/web/homepage/js/globalapi.js @@ -1,340 +1,340 @@ - -/*------------------ Date Function ------------------------*/ -function GetFullToday( ) -{ - var d=new Date(); - - var nday=d.getDate(); - var nmonth=d.getMonth()+1; - var nyear=d.getFullYear(); - - var strM=nmonth+''; - if( nmonth<10 ) - strM='0'+nmonth; - - var strD=nday+''; - if( nday<10 ) - strD='0'+nday; - - return nyear+'-'+strM+'-'+strD; -} - -function GetFullDate() -{ - var d=new Date(); - - var tDate={}; - - tDate.nyear=d.getFullYear(); - tDate.nmonth=d.getMonth()+1; - tDate.nday=d.getDate(); - - tDate.nhour=d.getHours(); - tDate.nminute=d.getMinutes(); - tDate.nsecond=d.getSeconds(); - - tDate.nweek=d.getDay(); - tDate.ndate=d.getDate(); - - var strM=tDate.nmonth+''; - if( tDate.nmonth<10 ) - strM='0'+tDate.nmonth; - - var strD=tDate.nday+''; - if( tDate.nday<10 ) - strD='0'+tDate.nday; - - var strH=tDate.nhour+''; - if( tDate.nhour<10 ) - strH='0'+tDate.nhour; - - var strMin=tDate.nminute+''; - if( tDate.nminute<10 ) - strMin='0'+tDate.nminute; - - var strS=tDate.nsecond+''; - if( tDate.nsecond<10 ) - strS='0'+tDate.nsecond; - - tDate.strdate=tDate.nyear+'-'+strM+'-'+strD; - tDate.strFulldate=tDate.strdate+' '+strH+':'+strMin+':'+strS; - - return tDate; -} - - -function Unixtimestamp2Date( nSecond ) -{ - var d=new Date(nSecond*1000); - - var tDate={}; - - tDate.nyear=d.getFullYear(); - tDate.nmonth=d.getMonth()+1; - tDate.nday=d.getDate(); - - tDate.nhour=d.getHours(); - tDate.nminute=d.getMinutes(); - tDate.nsecond=d.getSeconds(); - - tDate.nweek=d.getDay(); - tDate.ndate=d.getDate(); - - var strM=tDate.nmonth+''; - if( tDate.nmonth<10 ) - strM='0'+tDate.nmonth; - - var strD=tDate.nday+''; - if( tDate.nday<10 ) - strD='0'+tDate.nday; - - tDate.strdate=tDate.nyear+'-'+strM+'-'+strD; - - return tDate.strdate; -} - - -//------------Array Function------------- -Array.prototype.in_array = function (e) { - let sArray= ',' + this.join(this.S) + ','; - let skey=','+e+','; - - if(sArray.indexOf(skey)>=0) - return true; - else - return false; - } - - - -//------------String Function------------------ -/** -* Delete Left/Right Side Blank -*/ -String.prototype.trim=function() -{ - return this.replace(/(^\s*)|(\s*$)/g, ''); -} -/** -* Delete Left Side Blank -*/ -String.prototype.ltrim=function() -{ - return this.replace(/(^\s*)/g,''); -} -/** -* Delete Right Side Blank -*/ -String.prototype.rtrim=function() -{ - return this.replace(/(\s*$)/g,''); -} - - -//----------------Get Param------------- -function GetQueryString(name) -{ - var reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)"); - var r = window.location.search.substr(1).match(reg); - if (r!=null) - { - return unescape(r[2]); - } - else - { - return null; - } -} - -function GetGetStr() -{ - let strGet=""; - - //获取当前URL - let url = document.location.href; - - //获取?的位置 - let index = url.indexOf("?") - if(index != -1) { - //截取出?后面的字符串 - strGet = url.substr(index + 1); - } - - return strGet; -} - - -/*--------------------JSON Function------------*/ - -/* -功能:检查一个字符串是不是标准的JSON格式 -参数: strJson 被检查的字符串 -返回值: 如果字符串是一个标准的JSON格式,则返回JSON对象 - 如果字符串不是标准JSON格式,则返回null -*/ -function IsJson( strJson ) -{ - var tJson=null; - try - { - tJson=JSON.parse(strJson); - } - catch(exception) - { - return null; - } - - return tJson; -} - -/*-----------------------Ajax Function--------------------*/ -/*对JQuery的Ajax函数的封装,只支持异步 -参数说明: - url 目标地址 - action post/get - data 字符串格式的发送内容 - asyn true---异步模式;false-----同步模式; -*/ -function HttpReq( url,action, data,callbackfunc) -{ - var strAction=action.toLowerCase(); - - if( strAction=="post") - { - $.post(url,data,callbackfunc); - } - else if( strAction=="get") - { - $.get(url,callbackfunc); - } -} - -/*---------------Cookie Function-------------------*/ -function setCookie(name, value, time='',path='') { - if(time && path){ - var strsec = time * 1000; - var exp = new Date(); - exp.setTime(exp.getTime() + strsec * 1); - document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString() + ";path="+path; - }else if(time){ - var strsec = time * 1000; - var exp = new Date(); - exp.setTime(exp.getTime() + strsec * 1); - document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString(); - }else if(path){ - document.cookie = name + "=" + escape(value) + ";path="+path; - }else{ - document.cookie = name + "=" + escape(value); - } -} - -function getCookie(c_name) -{ - if(document.cookie.length > 0) { - c_start = document.cookie.indexOf(c_name + "=");//获取字符串的起点 - if(c_start != -1) { - c_start = c_start + c_name.length + 1;//获取值的起点 - c_end = document.cookie.indexOf(";", c_start);//获取结尾处 - if(c_end == -1) c_end = document.cookie.length;//如果是最后一个,结尾就是cookie字符串的结尾 - return decodeURI(document.cookie.substring(c_start, c_end));//截取字符串返回 - } - } - - return ""; -} - -function checkCookie(c_name) { - username = getCookie(c_name); - console.log(username); - if (username != null && username != "") - { return true; } - else - { return false; } -} - -function clearCookie(name) { - setCookie(name, "", -1); -} - - -/*--------Studio WX Message-------*/ -function IsInSlicer() -{ - let bMatch=navigator.userAgent.match( RegExp('BBL-Slicer','i') ); - - return bMatch; -} - - - -function SendWXMessage( strMsg ) -{ - let bCheck=IsInSlicer(); - - if(bCheck!=null) - { - window.wx.postMessage(strMsg); - } -} - - -/*------CSS Link Control----*/ -function RemoveCssLink( LinkPath ) -{ - let pNow=$("head link[href='"+LinkPath+"']"); - - let nTotal=pNow.length; - for( let n=0;n0) - RemoveCssLink(DarkCssPath); - } - else - { - if(nNow==0) - AddCssLink(DarkCssPath); - } -} - + +/*------------------ Date Function ------------------------*/ +function GetFullToday( ) +{ + var d=new Date(); + + var nday=d.getDate(); + var nmonth=d.getMonth()+1; + var nyear=d.getFullYear(); + + var strM=nmonth+''; + if( nmonth<10 ) + strM='0'+nmonth; + + var strD=nday+''; + if( nday<10 ) + strD='0'+nday; + + return nyear+'-'+strM+'-'+strD; +} + +function GetFullDate() +{ + var d=new Date(); + + var tDate={}; + + tDate.nyear=d.getFullYear(); + tDate.nmonth=d.getMonth()+1; + tDate.nday=d.getDate(); + + tDate.nhour=d.getHours(); + tDate.nminute=d.getMinutes(); + tDate.nsecond=d.getSeconds(); + + tDate.nweek=d.getDay(); + tDate.ndate=d.getDate(); + + var strM=tDate.nmonth+''; + if( tDate.nmonth<10 ) + strM='0'+tDate.nmonth; + + var strD=tDate.nday+''; + if( tDate.nday<10 ) + strD='0'+tDate.nday; + + var strH=tDate.nhour+''; + if( tDate.nhour<10 ) + strH='0'+tDate.nhour; + + var strMin=tDate.nminute+''; + if( tDate.nminute<10 ) + strMin='0'+tDate.nminute; + + var strS=tDate.nsecond+''; + if( tDate.nsecond<10 ) + strS='0'+tDate.nsecond; + + tDate.strdate=tDate.nyear+'-'+strM+'-'+strD; + tDate.strFulldate=tDate.strdate+' '+strH+':'+strMin+':'+strS; + + return tDate; +} + + +function Unixtimestamp2Date( nSecond ) +{ + var d=new Date(nSecond*1000); + + var tDate={}; + + tDate.nyear=d.getFullYear(); + tDate.nmonth=d.getMonth()+1; + tDate.nday=d.getDate(); + + tDate.nhour=d.getHours(); + tDate.nminute=d.getMinutes(); + tDate.nsecond=d.getSeconds(); + + tDate.nweek=d.getDay(); + tDate.ndate=d.getDate(); + + var strM=tDate.nmonth+''; + if( tDate.nmonth<10 ) + strM='0'+tDate.nmonth; + + var strD=tDate.nday+''; + if( tDate.nday<10 ) + strD='0'+tDate.nday; + + tDate.strdate=tDate.nyear+'-'+strM+'-'+strD; + + return tDate.strdate; +} + + +//------------Array Function------------- +Array.prototype.in_array = function (e) { + let sArray= ',' + this.join(this.S) + ','; + let skey=','+e+','; + + if(sArray.indexOf(skey)>=0) + return true; + else + return false; + } + + + +//------------String Function------------------ +/** +* Delete Left/Right Side Blank +*/ +String.prototype.trim=function() +{ + return this.replace(/(^\s*)|(\s*$)/g, ''); +} +/** +* Delete Left Side Blank +*/ +String.prototype.ltrim=function() +{ + return this.replace(/(^\s*)/g,''); +} +/** +* Delete Right Side Blank +*/ +String.prototype.rtrim=function() +{ + return this.replace(/(\s*$)/g,''); +} + + +//----------------Get Param------------- +function GetQueryString(name) +{ + var reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)"); + var r = window.location.search.substr(1).match(reg); + if (r!=null) + { + return unescape(r[2]); + } + else + { + return null; + } +} + +function GetGetStr() +{ + let strGet=""; + + //获取当前URL + let url = document.location.href; + + //获取?的位置 + let index = url.indexOf("?") + if(index != -1) { + //截取出?后面的字符串 + strGet = url.substr(index + 1); + } + + return strGet; +} + + +/*--------------------JSON Function------------*/ + +/* +功能:检查一个字符串是不是标准的JSON格式 +参数: strJson 被检查的字符串 +返回值: 如果字符串是一个标准的JSON格式,则返回JSON对象 + 如果字符串不是标准JSON格式,则返回null +*/ +function IsJson( strJson ) +{ + var tJson=null; + try + { + tJson=JSON.parse(strJson); + } + catch(exception) + { + return null; + } + + return tJson; +} + +/*-----------------------Ajax Function--------------------*/ +/*对JQuery的Ajax函数的封装,只支持异步 +参数说明: + url 目标地址 + action post/get + data 字符串格式的发送内容 + asyn true---异步模式;false-----同步模式; +*/ +function HttpReq( url,action, data,callbackfunc) +{ + var strAction=action.toLowerCase(); + + if( strAction=="post") + { + $.post(url,data,callbackfunc); + } + else if( strAction=="get") + { + $.get(url,callbackfunc); + } +} + +/*---------------Cookie Function-------------------*/ +function setCookie(name, value, time='',path='') { + if(time && path){ + var strsec = time * 1000; + var exp = new Date(); + exp.setTime(exp.getTime() + strsec * 1); + document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString() + ";path="+path; + }else if(time){ + var strsec = time * 1000; + var exp = new Date(); + exp.setTime(exp.getTime() + strsec * 1); + document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString(); + }else if(path){ + document.cookie = name + "=" + escape(value) + ";path="+path; + }else{ + document.cookie = name + "=" + escape(value); + } +} + +function getCookie(c_name) +{ + if(document.cookie.length > 0) { + c_start = document.cookie.indexOf(c_name + "=");//获取字符串的起点 + if(c_start != -1) { + c_start = c_start + c_name.length + 1;//获取值的起点 + c_end = document.cookie.indexOf(";", c_start);//获取结尾处 + if(c_end == -1) c_end = document.cookie.length;//如果是最后一个,结尾就是cookie字符串的结尾 + return decodeURI(document.cookie.substring(c_start, c_end));//截取字符串返回 + } + } + + return ""; +} + +function checkCookie(c_name) { + username = getCookie(c_name); + console.log(username); + if (username != null && username != "") + { return true; } + else + { return false; } +} + +function clearCookie(name) { + setCookie(name, "", -1); +} + + +/*--------Studio WX Message-------*/ +function IsInSlicer() +{ + let bMatch=navigator.userAgent.match( RegExp('BBL-Slicer','i') ); + + return bMatch; +} + + + +function SendWXMessage( strMsg ) +{ + let bCheck=IsInSlicer(); + + if(bCheck!=null) + { + window.wx.postMessage(strMsg); + } +} + + +/*------CSS Link Control----*/ +function RemoveCssLink( LinkPath ) +{ + let pNow=$("head link[href='"+LinkPath+"']"); + + let nTotal=pNow.length; + for( let n=0;n0) + RemoveCssLink(DarkCssPath); + } + else + { + if(nNow==0) + AddCssLink(DarkCssPath); + } +} + SwitchDarkMode("css/dark.css"); \ No newline at end of file diff --git a/resources/web/homepage/js/json2.js b/resources/web/homepage/js/json2.js index 70064f3825..94abd29c3b 100644 --- a/resources/web/homepage/js/json2.js +++ b/resources/web/homepage/js/json2.js @@ -1,185 +1,185 @@ -var JSON; -if (!JSON) { - JSON = {}; -} -(function () { - 'use strict'; - function f(n) { - // Format integers to have at least two digits. - return n < 10 ? '0' + n : n; - } - if (typeof Date.prototype.toJSON !== 'function') { - Date.prototype.toJSON = function (key) { - - return isFinite(this.valueOf()) - ? this.getUTCFullYear() + '-' + - f(this.getUTCMonth() + 1) + '-' + - f(this.getUTCDate()) + 'T' + - f(this.getUTCHours()) + ':' + - f(this.getUTCMinutes()) + ':' + - f(this.getUTCSeconds()) + 'Z' - : null; - }; - - String.prototype.toJSON = - Number.prototype.toJSON = - Boolean.prototype.toJSON = function (key) { - return this.valueOf(); - }; - } - var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, - escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, - gap, - indent, - meta = { // table of character substitutions - '\b': '\\b', - '\t': '\\t', - '\n': '\\n', - '\f': '\\f', - '\r': '\\r', - '"' : '\\"', - '\\': '\\\\' - }, - rep; - function quote(string) { - escapable.lastIndex = 0; - return escapable.test(string) ? '"' + string.replace(escapable, function (a) { - var c = meta[a]; - return typeof c === 'string' - ? c - : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }) + '"' : '"' + string + '"'; - } - function str(key, holder) { - var i, // The loop counter. - k, // The member key. - v, // The member value. - length, - mind = gap, - partial, - value = holder[key]; - if (value && typeof value === 'object' && - typeof value.toJSON === 'function') { - value = value.toJSON(key); - } - if (typeof rep === 'function') { - value = rep.call(holder, key, value); - } - switch (typeof value) { - case 'string': - return quote(value); - case 'number': - return isFinite(value) ? String(value) : 'null'; - case 'boolean': - case 'null': - return String(value); - case 'object': - if (!value) { - return 'null'; - } - gap += indent; - partial = []; - if (Object.prototype.toString.apply(value) === '[object Array]') { - length = value.length; - for (i = 0; i < length; i += 1) { - partial[i] = str(i, value) || 'null'; - } - v = partial.length === 0 - ? '[]' - : gap - ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' - : '[' + partial.join(',') + ']'; - gap = mind; - return v; - } - if (rep && typeof rep === 'object') { - length = rep.length; - for (i = 0; i < length; i += 1) { - if (typeof rep[i] === 'string') { - k = rep[i]; - v = str(k, value); - if (v) { - partial.push(quote(k) + (gap ? ': ' : ':') + v); - } - } - } - } else { - for (k in value) { - if (Object.prototype.hasOwnProperty.call(value, k)) { - v = str(k, value); - if (v) { - partial.push(quote(k) + (gap ? ': ' : ':') + v); - } - } - } - } - v = partial.length === 0 - ? '{}' - : gap - ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' - : '{' + partial.join(',') + '}'; - gap = mind; - return v; - } - } - if (typeof JSON.stringify !== 'function') { - JSON.stringify = function (value, replacer, space) { - var i; - gap = ''; - indent = ''; - if (typeof space === 'number') { - for (i = 0; i < space; i += 1) { - indent += ' '; - } - } else if (typeof space === 'string') { - indent = space; - } - rep = replacer; - if (replacer && typeof replacer !== 'function' && - (typeof replacer !== 'object' || - typeof replacer.length !== 'number')) { - throw new Error('JSON.stringify'); - } - return str('', {'': value}); - }; - } - if (typeof JSON.parse !== 'function') { - JSON.parse = function (text, reviver) { - var j; - function walk(holder, key) { - var k, v, value = holder[key]; - if (value && typeof value === 'object') { - for (k in value) { - if (Object.prototype.hasOwnProperty.call(value, k)) { - v = walk(value, k); - if (v !== undefined) { - value[k] = v; - } else { - delete value[k]; - } - } - } - } - return reviver.call(holder, key, value); - } - text = String(text); - cx.lastIndex = 0; - if (cx.test(text)) { - text = text.replace(cx, function (a) { - return '\\u' + - ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }); - } - if (/^[\],:{}\s]*$/ - .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') - .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') - .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { - j = eval('(' + text + ')'); - return typeof reviver === 'function' - ? walk({'': j}, '') - : j; - } - throw new SyntaxError('JSON.parse'); - }; - } -}()); +var JSON; +if (!JSON) { + JSON = {}; +} +(function () { + 'use strict'; + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + if (typeof Date.prototype.toJSON !== 'function') { + Date.prototype.toJSON = function (key) { + + return isFinite(this.valueOf()) + ? this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z' + : null; + }; + + String.prototype.toJSON = + Number.prototype.toJSON = + Boolean.prototype.toJSON = function (key) { + return this.valueOf(); + }; + } + var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + gap, + indent, + meta = { // table of character substitutions + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '"' : '\\"', + '\\': '\\\\' + }, + rep; + function quote(string) { + escapable.lastIndex = 0; + return escapable.test(string) ? '"' + string.replace(escapable, function (a) { + var c = meta[a]; + return typeof c === 'string' + ? c + : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }) + '"' : '"' + string + '"'; + } + function str(key, holder) { + var i, // The loop counter. + k, // The member key. + v, // The member value. + length, + mind = gap, + partial, + value = holder[key]; + if (value && typeof value === 'object' && + typeof value.toJSON === 'function') { + value = value.toJSON(key); + } + if (typeof rep === 'function') { + value = rep.call(holder, key, value); + } + switch (typeof value) { + case 'string': + return quote(value); + case 'number': + return isFinite(value) ? String(value) : 'null'; + case 'boolean': + case 'null': + return String(value); + case 'object': + if (!value) { + return 'null'; + } + gap += indent; + partial = []; + if (Object.prototype.toString.apply(value) === '[object Array]') { + length = value.length; + for (i = 0; i < length; i += 1) { + partial[i] = str(i, value) || 'null'; + } + v = partial.length === 0 + ? '[]' + : gap + ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' + : '[' + partial.join(',') + ']'; + gap = mind; + return v; + } + if (rep && typeof rep === 'object') { + length = rep.length; + for (i = 0; i < length; i += 1) { + if (typeof rep[i] === 'string') { + k = rep[i]; + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } else { + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } + v = partial.length === 0 + ? '{}' + : gap + ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' + : '{' + partial.join(',') + '}'; + gap = mind; + return v; + } + } + if (typeof JSON.stringify !== 'function') { + JSON.stringify = function (value, replacer, space) { + var i; + gap = ''; + indent = ''; + if (typeof space === 'number') { + for (i = 0; i < space; i += 1) { + indent += ' '; + } + } else if (typeof space === 'string') { + indent = space; + } + rep = replacer; + if (replacer && typeof replacer !== 'function' && + (typeof replacer !== 'object' || + typeof replacer.length !== 'number')) { + throw new Error('JSON.stringify'); + } + return str('', {'': value}); + }; + } + if (typeof JSON.parse !== 'function') { + JSON.parse = function (text, reviver) { + var j; + function walk(holder, key) { + var k, v, value = holder[key]; + if (value && typeof value === 'object') { + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = walk(value, k); + if (v !== undefined) { + value[k] = v; + } else { + delete value[k]; + } + } + } + } + return reviver.call(holder, key, value); + } + text = String(text); + cx.lastIndex = 0; + if (cx.test(text)) { + text = text.replace(cx, function (a) { + return '\\u' + + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }); + } + if (/^[\],:{}\s]*$/ + .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') + .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') + .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { + j = eval('(' + text + ')'); + return typeof reviver === 'function' + ? walk({'': j}, '') + : j; + } + throw new SyntaxError('JSON.parse'); + }; + } +}()); diff --git a/resources/web/include/globalapi.js b/resources/web/include/globalapi.js new file mode 100644 index 0000000000..706d122665 --- /dev/null +++ b/resources/web/include/globalapi.js @@ -0,0 +1,407 @@ + +/*------------------ Date Function ------------------------*/ +function GetFullToday( ) +{ + var d=new Date(); + + var nday=d.getDate(); + var nmonth=d.getMonth()+1; + var nyear=d.getFullYear(); + + var strM=nmonth+''; + if( nmonth<10 ) + strM='0'+nmonth; + + var strD=nday+''; + if( nday<10 ) + strD='0'+nday; + + return nyear+'-'+strM+'-'+strD; +} + +function GetFullDate() +{ + var d=new Date(); + + var tDate={}; + + tDate.nyear=d.getFullYear(); + tDate.nmonth=d.getMonth()+1; + tDate.nday=d.getDate(); + + tDate.nhour=d.getHours(); + tDate.nminute=d.getMinutes(); + tDate.nsecond=d.getSeconds(); + + tDate.nweek=d.getDay(); + tDate.ndate=d.getDate(); + + var strM=tDate.nmonth+''; + if( tDate.nmonth<10 ) + strM='0'+tDate.nmonth; + + var strD=tDate.nday+''; + if( tDate.nday<10 ) + strD='0'+tDate.nday; + + var strH=tDate.nhour+''; + if( tDate.nhour<10 ) + strH='0'+tDate.nhour; + + var strMin=tDate.nminute+''; + if( tDate.nminute<10 ) + strMin='0'+tDate.nminute; + + var strS=tDate.nsecond+''; + if( tDate.nsecond<10 ) + strS='0'+tDate.nsecond; + + tDate.strdate=tDate.nyear+'-'+strM+'-'+strD; + tDate.strFulldate=tDate.strdate+' '+strH+':'+strMin+':'+strS; + + return tDate; +} + + +function Unixtimestamp2Date( nSecond ) +{ + var d=new Date(nSecond*1000); + + var tDate={}; + + tDate.nyear=d.getFullYear(); + tDate.nmonth=d.getMonth()+1; + tDate.nday=d.getDate(); + + tDate.nhour=d.getHours(); + tDate.nminute=d.getMinutes(); + tDate.nsecond=d.getSeconds(); + + tDate.nweek=d.getDay(); + tDate.ndate=d.getDate(); + + var strM=tDate.nmonth+''; + if( tDate.nmonth<10 ) + strM='0'+tDate.nmonth; + + var strD=tDate.nday+''; + if( tDate.nday<10 ) + strD='0'+tDate.nday; + + tDate.strdate=tDate.nyear+'-'+strM+'-'+strD; + + return tDate.strdate; +} + + +//------------Array Function------------- +Array.prototype.in_array = function (e) { + let sArray= ',' + this.join(this.S) + ','; + let skey=','+e+','; + + if(sArray.indexOf(skey)>=0) + return true; + else + return false; + } + + + +//------------String Function------------------ +/** +* Delete Left/Right Side Blank +*/ +String.prototype.trim=function() +{ + return this.replace(/(^\s*)|(\s*$)/g, ''); +} +/** +* Delete Left Side Blank +*/ +String.prototype.ltrim=function() +{ + return this.replace(/(^\s*)/g,''); +} +/** +* Delete Right Side Blank +*/ +String.prototype.rtrim=function() +{ + return this.replace(/(\s*$)/g,''); +} + + +//----------------Get Param------------- +function GetQueryString(name) +{ + var reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)"); + var r = window.location.search.substr(1).match(reg); + if (r!=null) + { + return unescape(r[2]); + } + else + { + return null; + } +} + +function GetGetStr() +{ + let strGet=""; + + //获取当前URL + let url = document.location.href; + + //获取?的位置 + let index = url.indexOf("?") + if(index != -1) { + //截取出?后面的字符串 + strGet = url.substr(index + 1); + } + + return strGet; +} + +/*--------------File Function--------------*/ +function getFileName(path) +{ + var pos1 = path.lastIndexOf('/'); + var pos2 = path.lastIndexOf('\\'); + var pos = Math.max(pos1, pos2); + if (pos < 0) { + return null; + } + else + { + return path.substring(pos + 1); + } +} + +function getFileTail(path) +{ + var pos = path.lastIndexOf('.'); + if (pos < 0) { + return null; + } + else + { + return path.substring(pos + 1); + } +} + +/*--------------String Function-----------*/ +function html_encode(str) +{ + var s = ""; + if (str.length == 0) return ""; + s = str.replace(/&/g, "&"); + s = s.replace(//g, ">"); + s = s.replace(/ /g, " "); + s = s.replace(/\'/g, "'"); + s = s.replace(/\"/g, """); + s = s.replace(/\n/g, "
"); + + return s; +} + +function html_decode(str) +{ + var s = ""; + if (str.length == 0) return ""; + s = str.replace(/&/g, "&"); + s = s.replace(/</g, "<"); + s = s.replace(/>/g, ">"); + s = s.replace(/ /g, " "); + s = s.replace(/'/g, "\'"); + s = s.replace(/"/g, "\""); + s = s.replace(//g, "\n"); + + return s; +} + +/*--------------------JSON Function------------*/ + +/* +功能:检查一个字符串是不是标准的JSON格式 +参数: strJson 被检查的字符串 +返回值: 如果字符串是一个标准的JSON格式,则返回JSON对象 + 如果字符串不是标准JSON格式,则返回null +*/ +function IsJson( strJson ) +{ + var tJson=null; + try + { + tJson=JSON.parse(strJson); + } + catch(exception) + { + return null; + } + + return tJson; +} + +/*-----------------------Ajax Function--------------------*/ +/*对JQuery的Ajax函数的封装,只支持异步 +参数说明: + url 目标地址 + action post/get + data 字符串格式的发送内容 + asyn true---异步模式;false-----同步模式; +*/ +function HttpReq( url,action, data,callbackfunc) +{ + var strAction=action.toLowerCase(); + + if( strAction=="post") + { + $.post(url,data,callbackfunc); + } + else if( strAction=="get") + { + $.get(url,callbackfunc); + } +} + +/*---------------Cookie Function-------------------*/ +function setCookie(name, value, time='',path='') { + if(time && path){ + var strsec = time * 1000; + var exp = new Date(); + exp.setTime(exp.getTime() + strsec * 1); + document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString() + ";path="+path; + }else if(time){ + var strsec = time * 1000; + var exp = new Date(); + exp.setTime(exp.getTime() + strsec * 1); + document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString(); + }else if(path){ + document.cookie = name + "=" + escape(value) + ";path="+path; + }else{ + document.cookie = name + "=" + escape(value); + } +} + +function getCookie(c_name) +{ + if(document.cookie.length > 0) { + c_start = document.cookie.indexOf(c_name + "=");//获取字符串的起点 + if(c_start != -1) { + c_start = c_start + c_name.length + 1;//获取值的起点 + c_end = document.cookie.indexOf(";", c_start);//获取结尾处 + if(c_end == -1) c_end = document.cookie.length;//如果是最后一个,结尾就是cookie字符串的结尾 + return decodeURI(document.cookie.substring(c_start, c_end));//截取字符串返回 + } + } + + return ""; +} + +function checkCookie(c_name) { + username = getCookie(c_name); + console.log(username); + if (username != null && username != "") + { return true; } + else + { return false; } +} + +function clearCookie(name) { + setCookie(name, "", -1); +} + + +/*--------Studio WX Message-------*/ +function IsInSlicer() +{ + let bMatch=navigator.userAgent.match( RegExp('BBL-Slicer','i') ); + + return bMatch; +} + + + +function SendWXMessage( strMsg ) +{ + let bCheck=IsInSlicer(); + + if(bCheck!=null) + { + window.wx.postMessage(strMsg); + } +} + +function SendWXDebugInfo( strMsg ) +{ + var tSend={}; + tSend['sequence_id']=Math.round(new Date() / 1000); + tSend['command']="debug_info"; + tSend['msg']=strMsg; + + SendWXMessage( JSON.stringify(tSend) ); +} + + +/*------CSS Link Control----*/ +function RemoveCssLink( LinkPath ) +{ + let pNow=$("head link[href='"+LinkPath+"']"); + + let nTotal=pNow.length; + for( let n=0;n0) + RemoveCssLink(DarkCssPath); + } + else + { + if(nNow==0) + AddCssLink(DarkCssPath); + } +} + +SwitchDarkMode( "./css/dark.css" ); \ No newline at end of file diff --git a/resources/web/include/jquery-2.1.1.min.js b/resources/web/include/jquery-2.1.1.min.js new file mode 100644 index 0000000000..e3bf4d1b61 --- /dev/null +++ b/resources/web/include/jquery-2.1.1.min.js @@ -0,0 +1,4 @@ +/*! jQuery v2.1.1 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l=a.document,m="2.1.1",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return!n.isArray(a)&&a-parseFloat(a)>=0},isPlainObject:function(a){return"object"!==n.type(a)||a.nodeType||n.isWindow(a)?!1:a.constructor&&!j.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=l.createElement("script"),b.text=a,l.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:g.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(e=d.call(arguments,2),f=function(){return a.apply(b||this,e.concat(d.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:k}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+-new Date,v=a.document,w=0,x=0,y=gb(),z=gb(),A=gb(),B=function(a,b){return a===b&&(l=!0),0},C="undefined",D=1<<31,E={}.hasOwnProperty,F=[],G=F.pop,H=F.push,I=F.push,J=F.slice,K=F.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},L="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",N="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=N.replace("w","w#"),P="\\["+M+"*("+N+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+O+"))|)"+M+"*\\]",Q=":("+N+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+P+")*)|.*)\\)|)",R=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),S=new RegExp("^"+M+"*,"+M+"*"),T=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),V=new RegExp(Q),W=new RegExp("^"+O+"$"),X={ID:new RegExp("^#("+N+")"),CLASS:new RegExp("^\\.("+N+")"),TAG:new RegExp("^("+N.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+Q),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+L+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{I.apply(F=J.call(v.childNodes),v.childNodes),F[v.childNodes.length].nodeType}catch(eb){I={apply:F.length?function(a,b){H.apply(a,J.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],!a||"string"!=typeof a)return d;if(1!==(k=b.nodeType)&&9!==k)return[];if(p&&!e){if(f=_.exec(a))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return I.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return I.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=9===k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+qb(o[l]);w=ab.test(a)&&ob(b.parentNode)||b,x=o.join(",")}if(x)try{return I.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function gb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function hb(a){return a[u]=!0,a}function ib(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function jb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function kb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||D)-(~a.sourceIndex||D);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function lb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function nb(a){return hb(function(b){return b=+b,hb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function ob(a){return a&&typeof a.getElementsByTagName!==C&&a}c=fb.support={},f=fb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fb.setDocument=function(a){var b,e=a?a.ownerDocument||a:v,g=e.defaultView;return e!==n&&9===e.nodeType&&e.documentElement?(n=e,o=e.documentElement,p=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){m()},!1):g.attachEvent&&g.attachEvent("onunload",function(){m()})),c.attributes=ib(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ib(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(e.getElementsByClassName)&&ib(function(a){return a.innerHTML="
",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=ib(function(a){return o.appendChild(a).id=u,!e.getElementsByName||!e.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==C&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c=typeof a.getAttributeNode!==C&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==C?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==C&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(e.querySelectorAll))&&(ib(function(a){a.innerHTML="",a.querySelectorAll("[msallowclip^='']").length&&q.push("[*^$]="+M+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+M+"*(?:value|"+L+")"),a.querySelectorAll(":checked").length||q.push(":checked")}),ib(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+M+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ib(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",Q)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===v&&t(v,a)?-1:b===e||b.ownerDocument===v&&t(v,b)?1:k?K.call(k,a)-K.call(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],i=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:k?K.call(k,a)-K.call(k,b):0;if(f===g)return kb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?kb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},e):n},fb.matches=function(a,b){return fb(a,null,null,b)},fb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fb(b,n,null,[a]).length>0},fb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&E.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fb.selectors={cacheLength:50,createPseudo:hb,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+M+")"+a+"("+M+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==C&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?hb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=K.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:hb(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?hb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:hb(function(a){return function(b){return fb(a,b).length>0}}),contains:hb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:hb(function(a){return W.test(a||"")||fb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:nb(function(){return[0]}),last:nb(function(a,b){return[b-1]}),eq:nb(function(a,b,c){return[0>c?c+b:c]}),even:nb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:nb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:nb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:nb(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function rb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function sb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function tb(a,b,c){for(var d=0,e=b.length;e>d;d++)fb(a,b[d],c);return c}function ub(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function vb(a,b,c,d,e,f){return d&&!d[u]&&(d=vb(d)),e&&!e[u]&&(e=vb(e,f)),hb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||tb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ub(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ub(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?K.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ub(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):I.apply(g,r)})}function wb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=rb(function(a){return a===b},h,!0),l=rb(function(a){return K.call(b,a)>-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>i;i++)if(c=d.relative[a[i].type])m=[rb(sb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return vb(i>1&&sb(m),i>1&&qb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&wb(a.slice(i,e)),f>e&&wb(a=a.slice(e)),f>e&&qb(a))}m.push(c)}return sb(m)}function xb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=G.call(i));s=ub(s)}I.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&fb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?hb(f):f}return h=fb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xb(e,d)),f.selector=a}return f},i=fb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&ob(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qb(j),!a)return I.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&ob(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ib(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ib(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||jb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ib(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||jb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ib(function(a){return null==a.getAttribute("disabled")})||jb(L,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fb}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=n.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return g.call(b,a)>=0!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?n(a):a||[],!1).length}});var y,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=n.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:l,!0)),v.test(c[1])&&n.isPlainObject(b))for(c in b)n.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}return d=l.getElementById(c[2]),d&&d.parentNode&&(this.length=1,this[0]=d),this.context=l,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};A.prototype=n.fn,y=n(l);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};n.extend({dir:function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.unique(f):f)},index:function(a){return a?"string"==typeof a?g.call(n(a),this[0]):g.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.unique(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return n.dir(a,"parentNode")},parentsUntil:function(a,b,c){return n.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return n.dir(a,"nextSibling")},prevAll:function(a){return n.dir(a,"previousSibling")},nextUntil:function(a,b,c){return n.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return n.dir(a,"previousSibling",c)},siblings:function(a){return n.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return n.sibling(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(C[a]||n.unique(e),B.test(a)&&e.reverse()),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return n.each(a.match(E)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):n.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(b=a.memory&&l,c=!0,g=e||0,e=0,f=h.length,d=!0;h&&f>g;g++)if(h[g].apply(l[0],l[1])===!1&&a.stopOnFalse){b=!1;break}d=!1,h&&(i?i.length&&j(i.shift()):b?h=[]:k.disable())},k={add:function(){if(h){var c=h.length;!function g(b){n.each(b,function(b,c){var d=n.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&g(c)})}(arguments),d?f=h.length:b&&(e=c,j(b))}return this},remove:function(){return h&&n.each(arguments,function(a,b){var c;while((c=n.inArray(b,h,c))>-1)h.splice(c,1),d&&(f>=c&&f--,g>=c&&g--)}),this},has:function(a){return a?n.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],f=0,this},disable:function(){return h=i=b=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,b||k.disable(),this},locked:function(){return!i},fireWith:function(a,b){return!h||c&&!i||(b=b||[],b=[a,b.slice?b.slice():b],d?i.push(b):j(b)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!c}};return k},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&n.isFunction(a.promise)?e:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(H.resolveWith(l,[n]),n.fn.triggerHandler&&(n(l).triggerHandler("ready"),n(l).off("ready"))))}});function I(){l.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),n.ready()}n.ready.promise=function(b){return H||(H=n.Deferred(),"complete"===l.readyState?setTimeout(n.ready):(l.addEventListener("DOMContentLoaded",I,!1),a.addEventListener("load",I,!1))),H.promise(b)},n.ready.promise();var J=n.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)n.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f};n.acceptData=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=n.expando+Math.random()}K.uid=1,K.accepts=n.acceptData,K.prototype={key:function(a){if(!K.accepts(a))return 0;var b={},c=a[this.expando];if(!c){c=K.uid++;try{b[this.expando]={value:c},Object.defineProperties(a,b)}catch(d){b[this.expando]=c,n.extend(a,b)}}return this.cache[c]||(this.cache[c]={}),c},set:function(a,b,c){var d,e=this.key(a),f=this.cache[e];if("string"==typeof b)f[b]=c;else if(n.isEmptyObject(f))n.extend(this.cache[e],b);else for(d in b)f[d]=b[d];return f},get:function(a,b){var c=this.cache[this.key(a)];return void 0===b?c:c[b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=this.key(a),g=this.cache[f];if(void 0===b)this.cache[f]={};else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in g?d=[b,e]:(d=e,d=d in g?[d]:d.match(E)||[])),c=d.length;while(c--)delete g[d[c]]}},hasData:function(a){return!n.isEmptyObject(this.cache[a[this.expando]]||{})},discard:function(a){a[this.expando]&&delete this.cache[a[this.expando]]}};var L=new K,M=new K,N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}M.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return M.hasData(a)||L.hasData(a)},data:function(a,b,c){return M.access(a,b,c)},removeData:function(a,b){M.remove(a,b) +},_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J(this,function(b){var c,d=n.camelCase(a);if(f&&void 0===b){if(c=M.get(f,a),void 0!==c)return c;if(c=M.get(f,d),void 0!==c)return c;if(c=P(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=M.get(this,d);M.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&M.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){M.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=L.get(a,b),c&&(!d||n.isArray(c)?d=L.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return L.get(a,c)||L.access(a,c,{empty:n.Callbacks("once memory").add(function(){L.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthx",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";k.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|pointer|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof n!==U&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&(delete r.handle,L.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,m,o,p=[d||l],q=j.call(b,"type")?b.type:b,r=j.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||l,3!==d.nodeType&&8!==d.nodeType&&!X.test(q+n.event.triggered)&&(q.indexOf(".")>=0&&(r=q.split("."),q=r.shift(),r.sort()),k=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=r.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:n.makeArray(c,[b]),o=n.event.special[q]||{},e||!o.trigger||o.trigger.apply(d,c)!==!1)){if(!e&&!o.noBubble&&!n.isWindow(d)){for(i=o.delegateType||q,X.test(i+q)||(g=g.parentNode);g;g=g.parentNode)p.push(g),h=g;h===(d.ownerDocument||l)&&p.push(h.defaultView||h.parentWindow||a)}f=0;while((g=p[f++])&&!b.isPropagationStopped())b.type=f>1?i:o.bindType||q,m=(L.get(g,"events")||{})[b.type]&&L.get(g,"handle"),m&&m.apply(g,c),m=k&&g[k],m&&m.apply&&n.acceptData(g)&&(b.result=m.apply(g,c),b.result===!1&&b.preventDefault());return b.type=q,e||b.isDefaultPrevented()||o._default&&o._default.apply(p.pop(),c)!==!1||!n.acceptData(d)||k&&n.isFunction(d[q])&&!n.isWindow(d)&&(h=d[k],h&&(d[k]=null),n.event.triggered=q,d[q](),n.event.triggered=void 0,h&&(d[k]=h)),b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(L.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(g.namespace))&&(a.handleObj=g,a.data=g.data,e=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(a.result=e)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>=0:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]*)\/>/gi,bb=/<([\w:]+)/,cb=/<|&#?\w+;/,db=/<(?:script|style|link)/i,eb=/checked\s*(?:[^=]|=\s*.checked.)/i,fb=/^$|\/(?:java|ecma)script/i,gb=/^true\/(.*)/,hb=/^\s*\s*$/g,ib={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ib.optgroup=ib.option,ib.tbody=ib.tfoot=ib.colgroup=ib.caption=ib.thead,ib.th=ib.td;function jb(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function kb(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function lb(a){var b=gb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function mb(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function nb(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function ob(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pb(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}n.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=ob(h),f=ob(a),d=0,e=f.length;e>d;d++)pb(f[d],g[d]);if(b)if(c)for(f=f||ob(a),g=g||ob(h),d=0,e=f.length;e>d;d++)nb(f[d],g[d]);else nb(a,h);return g=ob(h,"script"),g.length>0&&mb(g,!i&&ob(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,o=a.length;o>m;m++)if(e=a[m],e||0===e)if("object"===n.type(e))n.merge(l,e.nodeType?[e]:e);else if(cb.test(e)){f=f||k.appendChild(b.createElement("div")),g=(bb.exec(e)||["",""])[1].toLowerCase(),h=ib[g]||ib._default,f.innerHTML=h[1]+e.replace(ab,"<$1>")+h[2],j=h[0];while(j--)f=f.lastChild;n.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===n.inArray(e,d))&&(i=n.contains(e.ownerDocument,e),f=ob(k.appendChild(e),"script"),i&&mb(f),c)){j=0;while(e=f[j++])fb.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f=n.event.special,g=0;void 0!==(c=a[g]);g++){if(n.acceptData(c)&&(e=c[L.expando],e&&(b=L.cache[e]))){if(b.events)for(d in b.events)f[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);L.cache[e]&&delete L.cache[e]}delete M.cache[c[M.expando]]}}}),n.fn.extend({text:function(a){return J(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(ob(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&mb(ob(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(ob(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!db.test(a)&&!ib[(bb.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(ab,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(ob(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(ob(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,m=this,o=l-1,p=a[0],q=n.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&eb.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(c=n.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=n.map(ob(c,"script"),kb),g=f.length;l>j;j++)h=c,j!==o&&(h=n.clone(h,!0,!0),g&&n.merge(f,ob(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,n.map(f,lb),j=0;g>j;j++)h=f[j],fb.test(h.type||"")&&!L.access(h,"globalEval")&&n.contains(i,h)&&(h.src?n._evalUrl&&n._evalUrl(h.src):n.globalEval(h.textContent.replace(hb,"")))}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),n(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qb,rb={};function sb(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function tb(a){var b=l,c=rb[a];return c||(c=sb(a,b),"none"!==c&&c||(qb=(qb||n("--> + + + + +
+
+ Accessories (0) +
+ + +
+
BOM (0)
+
+ +
+
+ +
+
Assembly Guide (0)
+
+ +
+
+ +
+
Other (0)
+
+ +
+
+ +
+ +
+
+
+
Profile name:xxxxxxxxxxx
+
Profile Author:xxxxxx
+
+
+ +
+
+ +
+
+
+
+
+ +
+
Profile description
+
+ +
+
+
+ +
 
+ + + + + + diff --git a/resources/web/model/model.css b/resources/web/model/model.css new file mode 100644 index 0000000000..6f7d3b92ac --- /dev/null +++ b/resources/web/model/model.css @@ -0,0 +1,341 @@ +* +{ + margin:0px; + padding: 0px; + border: 0px; + font-family: "system-ui", "Segoe UI", Roboto, Oxygen, Ubuntu, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-sans; +} + +html +{ + height: 100%; +} + +body +{ + min-width: 1280px; + height: 100%; + display:flex; + flex-direction: column; + justify-content: center; + align-items: center; + background-color: #F4F4F4; + font-size: 14px; + color: #595959; + line-height: 24px; +} + +#EmptyArea +{ + min-width: 1280px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + display: flex; +} + + +#WholeArea +{ + height: 100%; + position: relative; + overflow-y: hidden; + display: none; +} + +.ZScrol::-webkit-scrollbar {/*滚动条整体样式*/ + width: 8px; /*高宽分别对应横竖滚动条的尺寸*/ + height: 8px; + padding: 2px; +} + +.ZScrol::-webkit-scrollbar-thumb {/*滚动条里面小方块*/ + border-radius: 6px; + -webkit-box-shadow: inset 0 0 5px rgba(0,0,0,0.2); + box-shadow: inset 0 0 5px rgba(0,0,0,0.2); + background-color: #AAAAAA; +} + +.ZScrol::-webkit-scrollbar-track {/*滚动条里面轨道*/ + -webkit-box-shadow: inset 0 0 5px rgba(0,0,0,0.2); + box-shadow: inset 0 0 5px rgba(0,0,0,0.2); + border-radius: 10px; + background: #EDEDED; +} + + +.TopBottomBar +{ + height: 24px; +} + +.FloorBottomBar +{ + height: 100px; +} + + +/*----Basic----*/ +.Text_Title +{ + font-size: 16px; + font-weight: 700; + color: black; +} + +.Text_Value +{ + color: black; +} + +.Text_Bold +{ + font-weight: 700; +} + +.Text_TitleBoard +{ + border-bottom: 1px solid #DDDDDD; + padding: 16px 0px; +} + +.Text_TitleArea div +{ + height: 32px; + line-height: 32px +} + +.Block_BKGA +{ + background-color: white; +} + +.Block_BKGB +{ + background-color: #F4F4F4; +} + +/*---Swiper---*/ +.swiper +{ + --swiper-theme-color: #ff6600;/* 设置Swiper风格 */ + --swiper-navigation-color: #00AE42;/* 单独设置按钮颜色 */ + --swiper-navigation-size: 30px;/* 设置按钮大小 */ +} + +/*---Left---*/ +#LeftProcessBlock +{ + position:fixed; + top: 24px; + width: 264px; + height: 120px; + flex-shrink: 0; +} + +.LeftProcessBar +{ + border-left: 2px solid #DDDDDD; + height: 40px; + display: flex; + align-items: center; + cursor: pointer; +} + +.LeftProcessBar img +{ + margin-left:18px; +} + +.LeftProcessBar span +{ + margin-left: 20px; +} + +.LeftTipIcon +{ + width: 20px; + height: 20px; +} + +.ProcessBarSelected +{ + border-left-color:#00AE42; +} + +.ProcessBarSelected span +{ + color:#00AE42; +} + +#Info_ProcessBar.ProcessBarSelected img +{ + content:url("img/info_g.svg"); +} + +#File_ProcessBar.ProcessBarSelected img +{ + content:url("img/file_g.svg"); +} + +#Profile_ProcessBar.ProcessBarSelected img +{ + content:url("img/profile_g.svg"); +} + + + +/*---Right---*/ + +/*--Basic Info--*/ +#Info_Inside_Board +{ + display: flex; + height: calc(100% - 24px); + overflow-y:auto; +} + +.InfoBlock +{ + background-color: white; + padding: 20px 40px; +} + +.InfoBlock:nth-child(n+2) +{ + margin-top: 8px; +} + +#ModelInfoBlock +{ + width: 1000px; +} + +#Model_Name_Area { + display: flex; + justify-content: space-between; + align-items: flex-end; +} + +#Model_Preview_Image +{ + height: 480px; +} + +.swiper +{ + margin: 24px 0px; +} + +.swiper-slide +{ + cursor:pointer; + display: flex; + align-content: center; + justify-content: center; +} + +.swiper-slide img +{ + max-width:100%; + max-height:100%; +} + +/*---Document---*/ +#Model_Accessories +{ +} + +.File_Board +{ + background-color: #F4F4F4; + padding: 12px 15px; +} + +.File_Board:nth-child(n+2) +{ + margin-top: 16px; +} + +.FileListBoard +{ + display: flex; + flex-wrap: wrap; +} + +.FileItem +{ + width:406px; + background-color: white; + display: flex; + align-items: center; + padding: 12px 15px; + margin: 4px 0px; +} + +.FileItem:nth-child(2n+1) +{ + margin-right: 16px; +} + + +.FileIcon img +{ + width: 36px; + height: 42px; +} + +.ImageIcon img +{ + width: 36px; + height: 42px; + object-fit: cover; +} + +.FileText +{ + width: 306px; + height: 40px; + line-height: 20px; + padding: 0px 16px; + display: flex; + align-items: center; +} + +.FileSize +{ + display:none; +} + +.FileName +{ + width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.FileMenu +{ + height: 32px; + width: 32px; + display: flex; + cursor: pointer; + align-items: center; + justify-content: center; +} + +.FileMenu img +{ + width: 24px; +} + + +/*---Profile---*/ +#Profile_Preview_Image +{ + height: 480px; +} + + diff --git a/resources/web/model/model.js b/resources/web/model/model.js new file mode 100644 index 0000000000..caa91d29d5 --- /dev/null +++ b/resources/web/model/model.js @@ -0,0 +1,591 @@ +var ModelSwiper=null; +var ProfileSwiper=null; + +function OnInit() +{ + console.log(" 页面加载完成 "); + //翻译页面 + TranslatePage(); + + //获取数据 + RequestProjectInfo(); + + //图片滚动器的初始化 +// mySwiper = new Swiper('.swiper', { +// loop:true, +// slidesPerView : 4, +// slidesPerGroup : 1, +// spaceBetween: 8, +// navigation: { +// slidesPerGroup :4, +// nextEl: '.swiper-button-next', +// prevEl: '.swiper-button-prev', +// }, +// autoplay: { +// delay: 3000, +// stopOnLastSlide: false, +// disableOnInteraction: true, +// disableOnInteraction: false +// } +// }); + + //锚点跟踪 + AddScrollEvent(); + + //测试代码 + //ShowProjectInfo(null); + //ShowProjectInfo(TestProjectData); + //ShowProjectInfo(null); + //ShowProjectInfo(TestProjectData); +// $('#ModelPreviewList').viewer({ +// title: false, +// fullsreen: false, +// zIndex: 999999, +// interval: 3000 +// }); +// $('#ModelPreviewList').viewer('update'); +} + +function AddScrollEvent() +{ + //跟踪页面位置 + $('#Info_Inside_Board').scroll(function(){ + //checkElementDistance("Info_Inside_Board", 'Model_Basic') + //checkElementDistance("Info_Inside_Board", 'Model_Accessories'); + //checkElementDistance("Info_Inside_Board", 'Model_Profile'); + let ParentItem=$('#Info_Inside_Board'); + + let BItem=$('#Model_Basic'); + let AItem=$('#Model_Accessories'); + let PItem=$('#Model_Profile'); + + let BTop=Math.abs(BItem.offset().top - ParentItem.offset().top); + let BBottom=Math.abs(BItem.offset().top + BItem.innerHeight() - ParentItem.offset().top); + let ATop=Math.abs(AItem.offset().top - ParentItem.offset().top); + let ABottom=Math.abs(AItem.offset().top + AItem.innerHeight() - ParentItem.offset().top); + let PTop=Math.abs(PItem.offset().top - ParentItem.offset().top); + let PBottom=Math.abs(PItem.offset().top + PItem.innerHeight() - ParentItem.offset().top); + + console.log('------positon-----'); + console.log("B: "+BTop+'-'+BBottom); + console.log("A: "+ATop+'-'+ABottom); + console.log("P: "+PTop+'-'+PBottom); + + let nMin=Math.min(BTop,BBottom,ATop,ABottom,PTop,PBottom ); + + if( nMin==BTop || nMin==BBottom) + { + OnMenuSelected('Model_Basic'); + } + else if( nMin==ATop || nMin==ABottom) + { + OnMenuSelected('Model_Accessories'); + } + else if( nMin==PTop || nMin==PBottom) + { + OnMenuSelected('Model_Profile'); + } + + }); +} + +function OnMenuClick( strID ) +{ + scrollLocation("Info_Inside_Board",strID); + + //OnMenuSelected(strID); +} + +function OnMenuSelected(strID) +{ + console.log("MenuSelected: "+strID); + + //UI + $('.LeftProcessBar').removeClass('ProcessBarSelected'); + switch(strID) + { + case 'Model_Basic': + $('#Info_ProcessBar').addClass('ProcessBarSelected'); + break; + case 'Model_Accessories': + $('#File_ProcessBar').addClass('ProcessBarSelected'); + break; + case 'Model_Profile': + $('#Profile_ProcessBar').addClass('ProcessBarSelected'); + break; + } +} + + +/*-------------自动滚动跟踪效果---------*/ +function scrollLocation(FatherID, ChildID) +{ + let FItem=$('#'+FatherID); + let CItem=$('#'+ChildID); + + let Tmp=CItem.offset().top - FItem.offset().top + FItem.scrollTop(); + + // father.scrollTop( + // scrollTo.offset().top - father.offset().top + father.scrollTop() + // ); + // Or you can animate the scrolling: + FItem.animate({scrollTop:Tmp}, 400); +}; + +/*----------处理C++的消息-------*/ +function Request3MFInfo() +{ + var tSend={}; + tSend['sequence_id']=Math.round(new Date() / 1000); + tSend['command']="request_3mf_info"; + + SendWXMessage( JSON.stringify(tSend) ); +} + +function HandleStudio(pVal) +{ + let strCmd=pVal['command']; + + if(strCmd=='show_3mf_info') + { + ShowProjectInfo( pVal['model'] ); + } + else if(strCmd=='clear_3mf_info') + { + ShowProjectInfo( null ); + } +} + + +function ShowProjectInfo( p3MF ) +{ + if(p3MF==null) + { + $('#EmptyArea').css('display','flex'); + $('#WholeArea').hide(); + return; + } + + //Check Data + let pModel=p3MF['model']; + let pFile=p3MF['file']; + let pProfile=p3MF['profile']; + + ShowModelInfo( pModel ); + ShowFileInfo( pFile ); + ShowProfilelInfo(pProfile); + + TranslatePage(); + $('#EmptyArea').hide(); + $('#WholeArea').show(); + +// mySwiper = new Swiper('.swiper', { +// loop:true, +// spaceBetween: 8, +// navigation: { +// slidesPerGroup :4, +// nextEl: '.swiper-button-next', +// prevEl: '.swiper-button-prev', +// }, +// autoplay: { +// delay: 3000, +// stopOnLastSlide: false, +// disableOnInteraction: true, +// disableOnInteraction: false +// } +// }); + + AddScrollEvent(); +} + +function ShowModelInfo( pModel ) +{ + //==========Model Info========== + let sModelName=decodeURIComponent(pModel.name); + let sModelAuthor=decodeURIComponent(pModel.author); + let UploadType=pModel.upload_type.toLowerCase(); + let sLicence=pModel.license.toUpperCase(); + let sModelDesc=decodeURIComponent(pModel.description); + + SendWXDebugInfo("Model Name: "+sModelName); + + $('#ModelName').html(sModelName); + $('#ModelAuthorName').html(sModelAuthor); + + switch(UploadType) + { + case 'remix': + $('#ModelAuthorType').attr('tid','t93'); + break; + case 'shared': + $('#ModelAuthorType').attr('tid','t94'); + break; + case 'origin': + case 'profile': + default: + $('#ModelAuthorType').attr('tid','t92'); + break; + } + + switch(sLicence) + { + case 'CC0': + $('#ModelLicenceImg img').attr('src','img/cc-zero.png'); + $('#ModelLicenceImg').show(); + break; + case 'BY': + $('#ModelLicenceImg img').attr('src','img/by.png'); + $('#ModelLicenceImg').show(); + break; + case 'BY-SA': + $('#ModelLicenceImg img').attr('src','img/by-sa.png'); + $('#ModelLicenceImg').show(); + break; + case 'BY-ND': + $('#ModelLicenceImg img').attr('src','img/by-nd.png'); + $('#ModelLicenceImg').show(); + break; + case 'BY-NC': + $('#ModelLicenceImg img').attr('src','img/by-nc.png'); + $('#ModelLicenceImg').show(); + break; + case 'BY-NC-SA': + $('#ModelLicenceImg img').attr('src','img/by-nc-sa.png'); + $('#ModelLicenceImg').show(); + break; + case 'BY-NC-ND': + $('#ModelLicenceImg img').attr('src','img/by-nc-nd.png'); + $('#ModelLicenceImg').show(); + break; + default: + $('#ModelLicenceImg').hide(); + break; + } + + $('#Model_Desc').html( html_decode(sModelDesc) ); + + let ModelPreviewList=pModel.preview_img; + let TotalPreview=ModelPreviewList.length; + + if( ModelSwiper!=null ) + { + ModelSwiper.destroy(true,true); + ModelSwiper=null; + } + + if(TotalPreview>0) + { + let htmlPreview=''; + for(let pn=0;pn'; + } + + $('#ModelPreviewList').html(htmlPreview); + $('#ModelPreviewList').viewer({ + title: false, + fullsreen: false, + zIndex: 999999, + interval: 3000 + }); + $('#ModelPreviewList').viewer('update'); + + //Initial Swiper + if(TotalPreview==1) + { + ModelSwiper = new Swiper('#Model_Preview_Image.swiper', { + spaceBetween: 8 + }); + + $('#Model_Preview_Image .swiper-button-prev').hide(); + $('#Model_Preview_Image .swiper-button-next').hide(); + $('#Model_Preview_Image .swiper-pagination').hide(); + } + else + { + $('#Model_Preview_Image .swiper-button-prev').show(); + $('#Model_Preview_Image .swiper-button-next').show(); + $('#Model_Preview_Image .swiper-pagination').show(); + + ModelSwiper = new Swiper('#Model_Preview_Image.swiper', { + loop:true, + spaceBetween: 8, + navigation: { + nextEl: '.swiper-button-next', + prevEl: '.swiper-button-prev', + }, + autoplay: { + delay: 3000, + stopOnLastSlide: false, + disableOnInteraction: true, + disableOnInteraction: false + }, + pagination: { + el: '.swiper-pagination', + } + }); + } + + $('#Model_Preview_Image').show(); + } + else + { + $('#Model_Preview_Image').hide(); + } +} + +function ShowFileInfo( pFile ) +{ + let pBOM=pFile['BOM']; + let pAssembly=pFile['Assembly']; + let pOther=pFile['Other']; + + let BTotal=pBOM.length; + let ATotal=pAssembly.length; + let OTotal=pOther.length; + let fTotal=BTotal+ATotal+OTotal; + + //Total Number + $('#FileTotalNum').html(fTotal); + $('#BOMTotalNum').html(BTotal); + $('#AssemblyTotalNum').html(ATotal); + $('#OtherFileTotalNum').html(OTotal); + + //BOM + if(BTotal==0) + { + $('#FILE_BOM_List').hide(); + } + else + { + ConstructFileHtml('FILE_BOM_List',pBOM); + } + + //Assembly + if(ATotal==0) + { + $('#FILE_ASSEMBLY_List').hide(); + } + else + { + ConstructFileHtml('FILE_ASSEMBLY_List',pAssembly); + } + + //Other + if(OTotal==0) + { + $('#FILE_OTHER_List').hide(); + } + else + { + ConstructFileHtml('FILE_OTHER_List',pOther); + } + + //zIndex + $('.ImageIcon').viewer({ + title: false, + fullsreen: false, + zIndex: 999999, + interval: 3000 + }); + $('.ImageIcon').viewer('update'); +} + + +var ExcelTail=['xlsx','xlsm','xlsb','csv','xls','xltx','xltm','xlt','xlam','xla']; +var PdfTail=['pdf','fdf','xfdf','xdp','ppdf','ofd']; +var ImgTail=['jpg','jpeg','bmp','gif','svg','png','bmp']; + +var ImgID=0; + +function ConstructFileHtml( ID, pItem ) +{ + let fTotal=pItem.length; + + let strHtml=''; + for( let f=0;f=0 ) + { + strClass='ImageIcon'; + + ImgPath=tPath; + } + else if( $.inArray( sTail, ExcelTail )>=0 ) + { + ImgPath='img/excel.png'; + } + else if( $.inArray( sTail, PdfTail )>=0 ) + { + ImgPath='img/pdf.png'; + } + else + { + ImgPath='img/default.png'; + } + + //Add html + if( strClass!='ImageIcon' ) + { + strHtml+='
'+ + '
'+ + '
'+ + '
'+tName+'
'+ + '
'+ + '
'+ + '
'; + } + else + { + ImgID++; + let TmpImgID="AF"+ImgID; + + strHtml+='
'+ + '
'+ + '
'+ + '
'+tName+'
'+ + '
'+ + '
'+ + '
'; + } + } + + $('#'+ID+' .FileListBoard').html(strHtml); + + if( fTotal>0 ) + $('#'+ID).show(); +} + + +function ShowProfilelInfo( pProfile ) +{ + //==========Profile Info========== + let sProfileName=decodeURIComponent(pProfile.name); + let sProfileAuthor=decodeURIComponent(pProfile.author); + let sProfileDesc=decodeURIComponent(pProfile.description); + + $('#ProfileName').html(sProfileName); + $('#ProfileAuthor').html(sProfileAuthor); + + $('#Profile_Desc').html( html_decode(sProfileDesc) ); + + let ProfilePreviewList=pProfile.preview_img; + let TotalPreview=ProfilePreviewList.length; + + if( ProfileSwiper!=null ) + { + ProfileSwiper.destroy(true,true); + ProfileSwiper=null; + } + + if(TotalPreview>0) + { + let htmlPreview=''; + for(let pn=0;pn'; + } + + $('#ProfilePreviewList').html(htmlPreview); + $('#ProfilePreviewList').viewer({ + title: false, + fullsreen: false, + zIndex: 999999, + interval: 3000 + }); + $('#ProfilePreviewList').viewer("update"); + + //Init Profile Swiper + if(TotalPreview==1) + { + ProfileSwiper = new Swiper('#Profile_Preview_Image.swiper', { + spaceBetween: 8 + }); + + $('#Profile_Preview_Image .swiper-button-prev').hide(); + $('#Profile_Preview_Image .swiper-button-next').hide(); + $('#Profile_Preview_Image .swiper-pagination').hide(); + } + else + { + $('#Profile_Preview_Image .swiper-button-prev').show(); + $('#Profile_Preview_Image .swiper-button-next').show(); + $('#Profile_Preview_Image .swiper-pagination').show(); + + ProfileSwiper = new Swiper('#Profile_Preview_Image.swiper', { + loop:true, + spaceBetween: 8, + navigation: { + nextEl: '.swiper-button-next', + prevEl: '.swiper-button-prev', + }, + autoplay: { + delay: 3000, + stopOnLastSlide: false, + disableOnInteraction: true, + disableOnInteraction: false + }, + pagination: { + el: '.swiper-pagination', + } + }); + } + + $('#Profile_Preview_Image').show(); + } + else + { + $('#Profile_Preview_Image').hide(); + } +} + + +//Push Command to C++ +function RequestProjectInfo() +{ + var tSend={}; + tSend['sequence_id']=Math.round(new Date() / 1000); + tSend['command']="request_3mf_info"; + + SendWXMessage( JSON.stringify(tSend) ); +} + +function OnClickOpenFile( strFullPath ) +{ + var tSend={}; + tSend['sequence_id']=Math.round(new Date() / 1000); + tSend['command']="open_3mf_accessory"; + tSend['accessory_path']=strFullPath; + + SendWXMessage( JSON.stringify(tSend) ); + SendWXDebugInfo('----open accessory: '+strFullPath); +} + +function OnClickOpenImage( F_ID ) +{ + $("img#"+F_ID).click(); +} + + + + + + + + diff --git a/resources/web/model/test.js b/resources/web/model/test.js new file mode 100644 index 0000000000..3566871401 --- /dev/null +++ b/resources/web/model/test.js @@ -0,0 +1,124 @@ +var TestProjectData={ + "model": { + "name": "xxxxxxxxxxxxxxxxxxxxxxxxx easy print", + "author": "Zorro Zhang", + "upload_type": "share", + "license": "CC0", + "cover_img": "p1.png", + "preview_img": [ + { + "filepath": "http://bambuhtml.liuyishou.vip/img/p1.png", + "filename": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX.pdf", + "size": "232MB" + }, + { + "filepath": "http://bambuhtml.liuyishou.vip/img/p2.png", + "filename": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX.pdf", + "size": "232MB" + }, + { + "filepath": "http://bambuhtml.liuyishou.vip/img/m1.jpg", + "filename": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX.pdf", + "size": "232MB" + }, + { + "filepath": "http://bambuhtml.liuyishou.vip/img/m2.jpg", + "filename": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX.pdf", + "size": "232MB" + }, + { + "filepath": "http://bambuhtml.liuyishou.vip/img/m3.jpg", + "filename": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX.pdf", + "size": "232MB" + }, + { + "filepath": "http://bambuhtml.liuyishou.vip/img/m4.jpg", + "filename": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX.pdf", + "size": "232MB" + }, + { + "filepath": "http://bambuhtml.liuyishou.vip/img/m5.jpg", + "filename": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX.pdf", + "size": "232MB" + } + ], + "description": "This is a Description" + }, + "file": { + "BOM": [ + { + "filepath": "D:/AAA/bamboo_model/Tue_Feb_14/15_07_23#42936#55/Auxiliaries/Assembly Guide/XXXXXXXXXXXXXXXXXXXXXXXXXXXXX.pdf", + "filename": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX.pdf", + "size": "232MB" + }, + { + "filepath": "D:/AAA/bamboo_model/Tue_Feb_14/15_07_23#42936#55/Auxiliaries/Assembly Guide/Assembly Guide Pretty.xlsx", + "filename": "Assembly Guide Pretty.xlsx", + "size": "232MB" + }, + { + "filepath": "file://d:/preview.png", + "filename": "preview.png", + "size": "232MB" + } + ], + "Assembly": [ + { + "filepath": "D:/AAA/bamboo_model/Tue_Feb_14/15_07_23#42936#55/Auxiliaries/Assembly Guide/XXXXXXXXXXXXXXXXXXXXXXXXXXXXX.pdf", + "filename": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX.pdf", + "size": "232MB" + }, + { + "filepath": "D:/AAA/bamboo_model/Tue_Feb_14/15_07_23#42936#55/Auxiliaries/Assembly Guide/Assembly Guide Pretty.xlsx", + "filename": "Assembly Guide Pretty.xlsx", + "size": "232MB" + }, + { + "filepath": "D:/AAA/bamboo_model/Tue_Feb_14/15_07_23#42936#55/Auxiliaries/Assembly Guide/Assembly Guide Pretty.xlsx", + "filename": "Assembly Guide Pretty.xlsx", + "size": "232MB" + }, + { + "filepath": "file://d:/p2.png", + "filename": "p2.png", + "size": "232MB" + } + ], + "Other": [ + { + "filepath": "D:/AAA/bamboo_model/Tue_Feb_14/15_07_23#42936#55/Auxiliaries/Assembly Guide/XXXXXXXXXXXXXXXXXXXXXXXXXXXXX.pdf", + "filename": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX.pdf", + "size": "232MB" + }, + { + "filepath": "D:/AAA/bamboo_model/Tue_Feb_14/15_07_23#42936#55/Auxiliaries/Assembly Guide/Assembly Guide Pretty.xlsx", + "filename": "Assembly Guide Pretty.xlsx", + "size": "232MB" + }, + { + "filepath": "D:/AAA/bamboo_model/Tue_Feb_14/15_07_23#42936#55/Auxiliaries/Assembly Guide/BBB Guide Pretty.pdf", + "filename": "BBB Guide Pretty.pdf", + "size": "232MB" + }, + { + "filepath": "file://d:/p3.png", + "filename": "p3.png", + "size": "232MB" + } + ] + }, + "profile": { + "name": "xxxxxxxxxxxxxxxxxxxxxxxxx easy print", + "author": "Hally Potter", + "upload_type": "origin", + "cover_img": "p1.png", + "preview_img": [ + { + "filepath": "http://bambuhtml.liuyishou.vip/img/m1.jpg", + "filename": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX.pdf", + "size": "232MB" + } + ], + "description": "This is a profile Description" + } +}; \ No newline at end of file diff --git a/src/BambuStudio.cpp b/src/BambuStudio.cpp index 91b47f94d5..c1fd07e3ca 100644 --- a/src/BambuStudio.cpp +++ b/src/BambuStudio.cpp @@ -60,6 +60,7 @@ using namespace nlohmann; #include "libslic3r/Format/OBJ.hpp" #include "libslic3r/Format/SL1.hpp" #include "libslic3r/Utils.hpp" +#include "libslic3r/Time.hpp" #include "libslic3r/Thread.hpp" #include "libslic3r/BlacklistedLibraryCheck.hpp" @@ -230,24 +231,26 @@ typedef struct _cli_callback_mgr { lck.unlock(); return; } + int old_total_progress = m_total_progress; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": percent="<setup(debug_argc, debug_argv))*/ if (!this->setup(argc, argv)) @@ -403,13 +414,15 @@ int CLI::run(int argc, char **argv) boost::algorithm::iends_with(boost::filesystem::path(argv[0]).filename().string(), "gcodeviewer"); #endif // _WIN32*/ - bool translate_old = false; + bool translate_old = false, regenerate_thumbnails = false; int current_width, current_depth, current_height; - const std::vector &load_configs = m_config.option("load_settings", true)->values; + const std::vector &load_configs = m_config.option("load_settings", true)->values; //BBS: always use ForwardCompatibilitySubstitutionRule::Enable //const ForwardCompatibilitySubstitutionRule config_substitution_rule = m_config.option>("config_compatibility", true)->value; const ForwardCompatibilitySubstitutionRule config_substitution_rule = ForwardCompatibilitySubstitutionRule::Enable; - const std::vector &load_filaments = m_config.option("load_filaments", true)->values; + const std::vector &load_filaments = m_config.option("load_filaments", true)->values; + const std::vector &skip_objects = m_config.option("skip_objects", true)->values; + bool need_skip = (skip_objects.size() > 0)?true:false; if (start_gui) { BOOST_LOG_TRIVIAL(info) << "no action, start gui directly" << std::endl; @@ -480,7 +493,7 @@ int CLI::run(int argc, char **argv) PlateDataPtrs plate_data_src; int arrange_option; int plate_to_slice = 0, filament_count = 0; - bool first_file = true, is_bbl_3mf = false, need_arrange = true, has_thumbnails = false, up_config_to_date = false; + bool first_file = true, is_bbl_3mf = false, need_arrange = true, has_thumbnails = false, up_config_to_date = false, normative_check = true; Semver file_version; std::map orients_requirement; std::vector project_presets; @@ -488,22 +501,40 @@ int CLI::run(int argc, char **argv) std::vector upward_compatible_printers, new_print_compatible_printers, current_print_compatible_printers, current_different_settings; std::vector current_filaments_name, current_filaments_system_name, current_inherits_group; DynamicPrintConfig load_process_config, load_machine_config; + std::string pipe_name; // Read input file(s) if any. BOOST_LOG_TRIVIAL(info) << "Will start to read model file now, file count :" << m_input_files.size() << "\n"; ConfigOptionInt* slice_option = m_config.option("slice"); if (slice_option) plate_to_slice = slice_option->value; + + ConfigOptionBool* normative_check_option = m_config.option("normative_check"); + if (normative_check_option) + normative_check = normative_check_option->value; + ConfigOptionBool* uptodate_option = m_config.option("uptodate"); if (uptodate_option) up_config_to_date = uptodate_option->value; + + ConfigOptionString* pipe_option = m_config.option("pipe"); + if (pipe_option) { + pipe_name = pipe_option->value; + if (!pipe_name.empty()) { + BOOST_LOG_TRIVIAL(info) << boost::format("Will use pipe %1%")%pipe_name; +#if defined(__linux__) || defined(__LINUX__) + g_cli_callback_mgr.start(pipe_name); +#endif + } + } + /*for (const std::string& file : m_input_files) if (is_gcode_file(file) && boost::filesystem::exists(file)) { start_as_gcodeviewer = true; BOOST_LOG_TRIVIAL(info) << "found a gcode file:" << file << ", will start as gcode viewer\n"; break; }*/ - BOOST_LOG_TRIVIAL(info) << "plate_to_slice="<< plate_to_slice << std::endl; + BOOST_LOG_TRIVIAL(info) << boost::format("plate_to_slice=%1%, normative_check=%2%")%plate_to_slice %normative_check; //if (!start_as_gcodeviewer) { for (const std::string& file : m_input_files) { if (!boost::filesystem::exists(file)) { @@ -547,11 +578,26 @@ int CLI::run(int argc, char **argv) BOOST_LOG_TRIVIAL(info) << "object "<name <<", id :" << o->id().id << ", from bbl 3mf\n"; } - Semver old_version(1, 5, 9); + Semver old_version(1, 5, 9), old_version2(1, 5, 9); if ((file_version < old_version) && !config.empty()) { translate_old = true; BOOST_LOG_TRIVIAL(info) << boost::format("old 3mf version %1%, need to translate")%file_version.to_string(); } + if ((file_version < old_version2) && !config.empty()) { + regenerate_thumbnails = true; + BOOST_LOG_TRIVIAL(info) << boost::format("old 3mf version %1%, need to regenerate_thumbnails for all")%file_version.to_string(); + } + + if (normative_check) { + ConfigOptionStrings* postprocess_scripts = config.option("post_process"); + if (postprocess_scripts) { + std::vector postprocess_values = postprocess_scripts->values; + if (postprocess_values.size() > 0) { + BOOST_LOG_TRIVIAL(error) << boost::format("normative_check: postprocess not supported, array size %1%")%postprocess_values.size(); + flush_and_exit(CLI_POSTPROCESS_NOT_SUPPORTED); + } + } + } /*for (ModelObject *model_object : model.objects) for (ModelInstance *model_instance : model_object->instances) @@ -1022,7 +1068,7 @@ int CLI::run(int argc, char **argv) boost::nowide::cerr << __FUNCTION__<<": can not found option " <def(). @@ -1211,11 +1259,7 @@ int CLI::run(int argc, char **argv) } ConfigOptionVectorBase* opt_vec_dst = static_cast(opt); const ConfigOptionVectorBase* opt_vec_src = static_cast(source_opt); - if (opt_key == "compatible_prints" || opt_key == "compatible_printers" || opt_key == "model_id" || opt_key == "dev_model_name" || opt_key == "filament_settings_id") - continue; - else { - opt_vec_dst->set_at(opt_vec_src, filament_index-1, 0); - } + opt_vec_dst->set_at(opt_vec_src, filament_index-1, 0); } } } @@ -1331,6 +1375,7 @@ int CLI::run(int argc, char **argv) partplate_list.reset_size(current_width, current_depth, current_height, true, true); } } + /*for (ModelObject *model_object : m_models[0].objects) for (ModelInstance *model_instance : model_object->instances) { @@ -1345,6 +1390,13 @@ int CLI::run(int argc, char **argv) arrange_cfg.min_obj_distance = scaled(min_object_distance(m_print_config)); BOOST_LOG_TRIVIAL(info) << "will start transforms, commands count " << m_transforms.size() << "\n"; +#if defined(__linux__) || defined(__LINUX__) + if (g_cli_callback_mgr.is_started()) { + PrintBase::SlicingStatus slicing_status{1, "Loading files finished"}; + cli_status_callback(slicing_status); + } +#endif + for (auto const &opt_key : m_transforms) { BOOST_LOG_TRIVIAL(info) << "process transform " << opt_key << "\n"; if (opt_key == "merge") { @@ -1718,17 +1770,19 @@ int CLI::run(int argc, char **argv) // All transforms have been dealt with. Now ensure that the objects are on bed. // (Unless the user said otherwise.) - //BBS: current only support models on bed + //BBS: current only support models on bed, 0407 sinking supported //if (m_config.opt_bool("ensure_on_bed")) - for (auto &model : m_models) - for (auto &o : model.objects) - o->ensure_on_bed(); + // for (auto &model : m_models) + // for (auto &o : model.objects) + // o->ensure_on_bed(); // loop through action options bool export_to_3mf = false, load_slicedata = false, export_slicedata = false, export_slicedata_error = false; + bool no_check = false; std::string export_3mf_file, load_slice_data_dir, export_slice_data_dir; std::string outfile_dir = m_config.opt_string("outputdir"); std::vector calibration_thumbnails; + int max_slicing_time_per_plate = 0, max_triangle_count_per_plate = 0; for (auto const &opt_key : m_actions) { if (opt_key == "help") { this->print_help(); @@ -1737,10 +1791,7 @@ int CLI::run(int argc, char **argv) } else if (opt_key == "help_sla") { this->print_help(true, ptSLA); } else if (opt_key == "pipe") { -#if defined(__linux__) || defined(__LINUX__) - std::string pipe_name = m_config.option("pipe")->value; - g_cli_callback_mgr.start(pipe_name); -#endif + //already processed before } else if (opt_key == "load_slicedata") { load_slicedata = true; load_slice_data_dir = m_config.opt_string(opt_key); @@ -1761,6 +1812,10 @@ int CLI::run(int argc, char **argv) } } else if (opt_key == "uptodate") { //already processed before + } else if (opt_key == "mtcpp") { + max_triangle_count_per_plate = m_config.option("mtcpp")->value; + } else if (opt_key == "mstpp") { + max_slicing_time_per_plate = m_config.option("mstpp")->value; } else if (opt_key == "export_stl") { for (auto &model : m_models) model.add_default_instances(); @@ -1777,7 +1832,11 @@ int CLI::run(int argc, char **argv) } */else if (opt_key == "export_3mf") { export_to_3mf = true; export_3mf_file = m_config.opt_string(opt_key); + }else if(opt_key=="no_check"){ + no_check = m_config.opt_bool(opt_key); //} else if (opt_key == "export_gcode" || opt_key == "export_sla" || opt_key == "slice") { + } else if (opt_key == "normative_check") { + //already processed before } else if (opt_key == "export_slicedata") { export_slicedata = true; export_slice_data_dir = m_config.opt_string(opt_key); @@ -1788,6 +1847,21 @@ int CLI::run(int argc, char **argv) } else if (opt_key == "slice") { //BBS: slice 0 means all plates, i means plate i; plate_to_slice = m_config.option("slice")->value; + bool pre_check = (plate_to_slice == 0)?true:false; + if (partplate_list.get_plate_count() == 1) + pre_check = false; + bool finished = false; + + //skip model object + std::map skip_maps; + if (need_skip) { + BOOST_LOG_TRIVIAL(info) << boost::format("need to skip objects, size %1%:")%skip_objects.size(); + for (int index = 0; index < skip_objects.size(); index++) + { + skip_maps[skip_objects[index]] = false; + BOOST_LOG_TRIVIAL(info) << boost::format("object %1%, id %2%")%index %skip_objects[index]; + } + } /*if (opt_key == "export_gcode" && printer_technology == ptSLA) { boost::nowide::cerr << "error: cannot export G-code for an FFF configuration" << std::endl; flush_and_exit(1); @@ -1796,6 +1870,12 @@ int CLI::run(int argc, char **argv) flush_and_exit(1); }*/ BOOST_LOG_TRIVIAL(info) << "Need to slice for plate "<= 0) // FIXME: is this sufficient? - printf("%3d%s %s\n", s.percent, "% =>", s.text.c_str()); - });*/ - //BBS: slice every partplate one by one - PrintBase *print=NULL; - Slic3r::GUI::GCodeResult *gcode_result = NULL; - int print_index; - for (int index = 0; index < partplate_list.get_plate_count(); index ++) + while(!finished) { - if ((plate_to_slice != 0) && (plate_to_slice != (index + 1))) { - BOOST_LOG_TRIVIAL(info) << "Skip plate " << index+1 << std::endl; - continue; - } - //get the current partplate - Slic3r::GUI::PartPlate* part_plate = partplate_list.get_plate(index); - part_plate->get_print(&print, &gcode_result, &print_index); - /*if (outfile_config.empty()) + //BBS: slice every partplate one by one + PrintBase *print=NULL; + Slic3r::GUI::GCodeResult *gcode_result = NULL; + int print_index; + for (int index = 0; index < partplate_list.get_plate_count(); index ++) { - outfile = "plate_" + std::to_string(index + 1) + ".gcode"; - } - else - { - outfile = "plate_" + std::to_string(index + 1) + "_" + outfile_config + ".gcode"; - }*/ + if ((plate_to_slice != 0) && (plate_to_slice != (index + 1))) { + BOOST_LOG_TRIVIAL(info) << "Skip plate " << index+1 << std::endl; + continue; + } + BOOST_LOG_TRIVIAL(info) << boost::format("Plate %1%: pre_check %2%, start")%(index+1)%pre_check; + long long start_time = 0, end_time = 0; + start_time = (long long)Slic3r::Utils::get_current_time_utc(); + //get the current partplate + Slic3r::GUI::PartPlate* part_plate = partplate_list.get_plate(index); + part_plate->get_print(&print, &gcode_result, &print_index); + /*if (outfile_config.empty()) + { + outfile = "plate_" + std::to_string(index + 1) + ".gcode"; + } + else + { + outfile = "plate_" + std::to_string(index + 1) + "_" + outfile_config + ".gcode"; + }*/ - //update plate's bounding box to model + //update plate's bounding box to model #if 0 - BoundingBoxf3 print_volume = part_plate->get_bounding_box(false); - print_volume.max(2) = z; - print_volume.min(2) = -1e10; - model.update_print_volume_state(print_volume); - BOOST_LOG_TRIVIAL(info) << boost::format("print_volume {%1%,%2%,%3%}->{%4%, %5%, %6%}") % print_volume.min(0) % print_volume.min(1) - % print_volume.min(2) % print_volume.max(0) % print_volume.max(1) % print_volume.max(2) << std::endl; + BoundingBoxf3 print_volume = part_plate->get_bounding_box(false); + print_volume.max(2) = z; + print_volume.min(2) = -1e10; + model.update_print_volume_state(print_volume); + BOOST_LOG_TRIVIAL(info) << boost::format("print_volume {%1%,%2%,%3%}->{%4%, %5%, %6%}") % print_volume.min(0) % print_volume.min(1) + % print_volume.min(2) % print_volume.max(0) % print_volume.max(1) % print_volume.max(2) << std::endl; #else - BuildVolume build_volume(part_plate->get_shape(), print_height); - model.update_print_volume_state(build_volume); - unsigned int count = model.update_print_volume_state(build_volume); + BuildVolume build_volume(part_plate->get_shape(), print_height); + //model.update_print_volume_state(build_volume); + unsigned int count = model.update_print_volume_state(build_volume); - if (count == 0) { - BOOST_LOG_TRIVIAL(error) << "plate "<< index+1<< ": Nothing to be sliced, Either the print is empty or no object is fully inside the print volume before apply." << std::endl; - flush_and_exit(CLI_NO_SUITABLE_OBJECTS); - } - else { - for (ModelObject* model_object : model.objects) - for (ModelInstance *i : model_object->instances) - if (i->print_volume_state == ModelInstancePVS_Partly_Outside) + if (count == 0) { + BOOST_LOG_TRIVIAL(error) << "plate "<< index+1<< ": Nothing to be sliced, Either the print is empty or no object is fully inside the print volume before apply." << std::endl; + flush_and_exit(CLI_NO_SUITABLE_OBJECTS); + } + else if ((plate_to_slice != 0) || pre_check) { + long long triangle_count = 0; + int printable_instances = 0; + int skipped_count = 0; + for (ModelObject* model_object : model.objects) + for (ModelInstance *i : model_object->instances) { - BOOST_LOG_TRIVIAL(error) << "plate "<< index+1<< ": Found Object " << model_object->name <<" partly inside, can not be sliced." << std::endl; - flush_and_exit(CLI_OBJECTS_PARTLY_INSIDE); - } - } - // BBS: TODO - //BOOST_LOG_TRIVIAL(info) << boost::format("print_volume {%1%,%2%,%3%}->{%4%, %5%, %6%}, has %7% printables") % print_volume.min(0) % print_volume.min(1) - // % print_volume.min(2) % print_volume.max(0) % print_volume.max(1) % print_volume.max(2) % count << std::endl; -#endif + if (skip_maps.find(i->loaded_id) != skip_maps.end()) { + skip_maps[i->loaded_id] = true; + i->printable = false; + if (i->print_volume_state == ModelInstancePVS_Inside) { + skipped_count++; + BOOST_LOG_TRIVIAL(info) << boost::format("Plate %1%: skip object %2%.")%(index+1)%i->loaded_id; + //need to regenerate the thumbnail + if (plate_data_src.size() > index) { + if (!plate_data_src[index]->thumbnail_file.empty()) { + BOOST_LOG_TRIVIAL(info) << boost::format("Plate %1%: clear loaded thumbnail %2%.")%(index+1)%plate_data_src[index]->thumbnail_file; + plate_data_src[index]->thumbnail_file.clear(); + } + if (!plate_data_src[index]->top_file.empty()) { + BOOST_LOG_TRIVIAL(info) << boost::format("Plate %1%: clear loaded top_thumbnail %2%.")%(index+1)%plate_data_src[index]->top_file; + plate_data_src[index]->top_file.clear(); + } + if (!plate_data_src[index]->pick_file.empty()) { + BOOST_LOG_TRIVIAL(info) << boost::format("Plate %1%: clear loaded pick_thumbnail %2%.")%(index+1)%plate_data_src[index]->pick_file; + plate_data_src[index]->pick_file.clear(); + } + } + } + continue; + } - //PrintBase *print = (printer_technology == ptFFF) ? static_cast(&fff_print) : static_cast(&sla_print); - /*if (! m_config.opt_bool("dont_arrange")) { - if (user_center_specified) { - Vec2d c = m_config.option("center")->value; - arrange_objects(model, InfiniteBed{scaled(c)}, arrange_cfg); - } else - arrange_objects(model, bed, arrange_cfg); - }*/ - /*if (printer_technology == ptFFF) { - for (auto* mo : model.objects) - (dynamic_cast(print))->auto_assign_extruders(mo); - } else { - // The default for "filename_format" is good for FDM: "[input_filename_base].gcode" - // Replace it with a reasonable SLA default. - std::string &format = m_print_config.opt_string("filename_format", true); - if (format == static_cast(m_print_config.def()->get("filename_format")->default_value.get())->value) - format = "[input_filename_base].SL1"; - }*/ - DynamicPrintConfig new_print_config = m_print_config; - new_print_config.apply(*part_plate->config()); - new_print_config.apply(m_extra_config, true); - print->apply(model, new_print_config); - StringObjectException warning; - auto err = print->validate(&warning); - if (!err.string.empty()) { - BOOST_LOG_TRIVIAL(info) << "got error when validate: "<< err.string << std::endl; - boost::nowide::cerr << err.string << std::endl; - //BBS: continue for other plates - //continue; - flush_and_exit(CLI_VALIDATE_ERROR); - } - else if (!warning.string.empty()) - BOOST_LOG_TRIVIAL(info) << "got warnings: "<< warning.string << std::endl; + if (i->print_volume_state == ModelInstancePVS_Partly_Outside) + { + BOOST_LOG_TRIVIAL(error) << "plate "<< index+1<< ": Found Object " << model_object->name <<" partly inside, can not be sliced." << std::endl; + flush_and_exit(CLI_OBJECTS_PARTLY_INSIDE); + } + else if ((max_triangle_count_per_plate != 0) && (i->print_volume_state == ModelInstancePVS_Inside)) + { + for (const ModelVolume* vol : model_object->volumes) + { + if (vol->is_model_part()) { + size_t volume_triangle_count = vol->mesh().facets_count(); + triangle_count += volume_triangle_count; + BOOST_LOG_TRIVIAL(info) << boost::format("volume triangle count %1%, total %2%")%volume_triangle_count %triangle_count; + if (triangle_count > max_triangle_count_per_plate) + { + BOOST_LOG_TRIVIAL(error) << "plate "<< index+1<< ": triangle count " << triangle_count <<" exceeds the limit:" << max_triangle_count_per_plate; + flush_and_exit(CLI_TRIANGLE_COUNT_EXCEEDS_LIMIT); + } + } + } + } - if (print->empty()) { - BOOST_LOG_TRIVIAL(error) << "plate "<< index+1<< ": Nothing to be sliced, Either the print is empty or no object is fully inside the print volume after apply." << std::endl; - flush_and_exit(CLI_NO_SUITABLE_OBJECTS); - } - else - try { - std::string outfile_final; - BOOST_LOG_TRIVIAL(info) << "start Print::process for partplate "<set_status_callback(cli_status_callback); - g_cli_callback_mgr.set_plate_info(index+1, (plate_to_slice== 0)?partplate_list.get_plate_count():1); - if (!warning.string.empty()) { - PrintBase::SlicingStatus slicing_status{2, warning.string, 0, 0}; - cli_status_callback(slicing_status); + if (i->print_volume_state == ModelInstancePVS_Inside) + printable_instances++; } + + if (printable_instances == 0) { + BOOST_LOG_TRIVIAL(error) << "plate "<< index+1<< ": Nothing to be sliced, after skipping "<{%4%, %5%, %6%}, has %7% printables") % print_volume.min(0) % print_volume.min(1) + // % print_volume.min(2) % print_volume.max(0) % print_volume.max(1) % print_volume.max(2) % count << std::endl; #endif - if (load_slicedata) { - std::string plate_dir = load_slice_data_dir+"/"+std::to_string(index+1); - int ret = print->load_cached_data(plate_dir); - if (ret) { - BOOST_LOG_TRIVIAL(warning) << "plate "<< index+1<< ": load Slicing data error, ret=" << ret; - BOOST_LOG_TRIVIAL(warning) << "plate "<< index+1<< ": switch normal slicing"; + DynamicPrintConfig new_print_config = m_print_config; + new_print_config.apply(*part_plate->config()); + new_print_config.apply(m_extra_config, true); + print->apply(model, new_print_config); + BOOST_LOG_TRIVIAL(info) << boost::format("set no_check to %1%:")%no_check; + print->set_no_check_flag(no_check);//BBS + StringObjectException warning; + auto err = print->validate(&warning); + if (!err.string.empty()) { + BOOST_LOG_TRIVIAL(info) << "got error when validate: "<< err.string << std::endl; + boost::nowide::cerr << err.string << std::endl; + //BBS: continue for other plates + //continue; + flush_and_exit(CLI_VALIDATE_ERROR); + } + else if (!warning.string.empty()) + BOOST_LOG_TRIVIAL(info) << "got warnings: "<< warning.string << std::endl; + + if (print->empty()) { + BOOST_LOG_TRIVIAL(error) << "plate "<< index+1<< ": Nothing to be sliced, Either the print is empty or no object is fully inside the print volume after apply." << std::endl; + flush_and_exit(CLI_NO_SUITABLE_OBJECTS); + } + else { + if (pre_check) //continue to next plate directly + continue; + try { + std::string outfile_final; + BOOST_LOG_TRIVIAL(info) << "start Print::process for partplate "<set_status_callback(cli_status_callback); + g_cli_callback_mgr.set_plate_info(index+1, (plate_to_slice== 0)?partplate_list.get_plate_count():1); + if (!warning.string.empty()) { + PrintBase::SlicingStatus slicing_status{4, warning.string, 0, 0}; + cli_status_callback(slicing_status); + } + else { + PrintBase::SlicingStatus slicing_status{4, "Slicing begins"}; + cli_status_callback(slicing_status); + } + } +#endif + if (load_slicedata) { + std::string plate_dir = load_slice_data_dir+"/"+std::to_string(index+1); + int ret = print->load_cached_data(plate_dir); + if (ret) { + BOOST_LOG_TRIVIAL(warning) << "plate "<< index+1<< ": load Slicing data error, ret=" << ret; + BOOST_LOG_TRIVIAL(warning) << "plate "<< index+1<< ": switch normal slicing"; + print->process(); + } + else { + BOOST_LOG_TRIVIAL(info) << "plate "<< index+1<< ": load cached data success, go on."; +#if defined(__linux__) || defined(__LINUX__) + if (g_cli_callback_mgr.is_started()) { + PrintBase::SlicingStatus slicing_status{69, "Cache data loaded"}; + cli_status_callback(slicing_status); + } +#endif + print->process(true); + BOOST_LOG_TRIVIAL(info) << "plate "<< index+1<< ": finished print::process."; + } + } + else { print->process(); } - else { - BOOST_LOG_TRIVIAL(info) << "plate "<< index+1<< ": load cached data success, go on."; - print->process(true); - BOOST_LOG_TRIVIAL(info) << "plate "<< index+1<< ": finished print::process."; - } - } - else { - print->process(); - } - if (printer_technology == ptFFF) { - // The outfile is processed by a PlaceholderParser. - //outfile = part_plate->get_tmp_gcode_path(); - if (outfile_dir.empty()) { - outfile = part_plate->get_tmp_gcode_path(); - } - else { - outfile = outfile_dir + "/plate_" + std::to_string(index + 1) + ".gcode"; - part_plate->set_tmp_gcode_path(outfile); - } - BOOST_LOG_TRIVIAL(info) << "process finished, will export gcode temporily to " << outfile << std::endl; - outfile = (dynamic_cast(print))->export_gcode(outfile, gcode_result, nullptr); - //outfile_final = (dynamic_cast(print))->print_statistics().finalize_output_path(outfile); - //m_fff_print->export_gcode(m_temp_output_path, m_gcode_result, [this](const ThumbnailsParams& params) { return this->render_thumbnails(params); }); - }/* else { - outfile = sla_print.output_filepath(outfile); - // We need to finalize the filename beforehand because the export function sets the filename inside the zip metadata - outfile_final = sla_print.print_statistics().finalize_output_path(outfile); - sla_archive.export_print(outfile_final, sla_print); - }*/ - /*if (outfile != outfile_final) { - if (Slic3r::rename_file(outfile, outfile_final)) { - boost::nowide::cerr << "Renaming file " << outfile << " to " << outfile_final << " failed" << std::endl; - flush_and_exit(1); - } - outfile = outfile_final; - }*/ - // Run the post-processing scripts if defined. - run_post_process_scripts(outfile, print->full_print_config()); - BOOST_LOG_TRIVIAL(info) << "Slicing result exported to " << outfile << std::endl; - part_plate->update_slice_result_valid_state(true); + if (printer_technology == ptFFF) { + std::string conflict_result = dynamic_cast(print)->get_conflict_string(); + if (!conflict_result.empty()) { + BOOST_LOG_TRIVIAL(error) << "plate "<< index+1<< ": found slicing result conflict!"<< std::endl; + flush_and_exit(CLI_GCODE_PATH_CONFLICTS); + } + // The outfile is processed by a PlaceholderParser. + //outfile = part_plate->get_tmp_gcode_path(); + if (outfile_dir.empty()) { + outfile = part_plate->get_tmp_gcode_path(); + } + else { + outfile = outfile_dir + "/plate_" + std::to_string(index + 1) + ".gcode"; + part_plate->set_tmp_gcode_path(outfile); + } + BOOST_LOG_TRIVIAL(info) << "process finished, will export gcode temporily to " << outfile << std::endl; + outfile = (dynamic_cast(print))->export_gcode(outfile, gcode_result, nullptr); + //outfile_final = (dynamic_cast(print))->print_statistics().finalize_output_path(outfile); + //m_fff_print->export_gcode(m_temp_output_path, m_gcode_result, [this](const ThumbnailsParams& params) { return this->render_thumbnails(params); }); + }/* else { + outfile = sla_print.output_filepath(outfile); + // We need to finalize the filename beforehand because the export function sets the filename inside the zip metadata + outfile_final = sla_print.print_statistics().finalize_output_path(outfile); + sla_archive.export_print(outfile_final, sla_print); + }*/ + /*if (outfile != outfile_final) { + if (Slic3r::rename_file(outfile, outfile_final)) { + boost::nowide::cerr << "Renaming file " << outfile << " to " << outfile_final << " failed" << std::endl; + flush_and_exit(1); + } + outfile = outfile_final; + }*/ + // Run the post-processing scripts if defined. + //run_post_process_scripts(outfile, print->full_print_config()); + BOOST_LOG_TRIVIAL(info) << "Slicing result exported to " << outfile << std::endl; + part_plate->update_slice_result_valid_state(true); #if defined(__linux__) || defined(__LINUX__) - if (g_cli_callback_mgr.is_started()) { - PrintBase::SlicingStatus slicing_status{100, "Slicing finished"}; - cli_status_callback(slicing_status); - } -#endif - if (export_slicedata) { - BOOST_LOG_TRIVIAL(info) << "plate "<< index+1<< ":will export Slicing data to " << export_slice_data_dir; - std::string plate_dir = export_slice_data_dir+"/"+std::to_string(index+1); - bool with_space = (get_logging_level() >= 4)?true:false; - int ret = print->export_cached_data(plate_dir, with_space); - if (ret) { - BOOST_LOG_TRIVIAL(error) << "plate "<< index+1<< ": export Slicing data error, ret=" << ret; - export_slicedata_error = true; - if (fs::exists(plate_dir)) - fs::remove_all(plate_dir); + if (g_cli_callback_mgr.is_started()) { + PrintBase::SlicingStatus slicing_status{100, "Slicing finished"}; + cli_status_callback(slicing_status); } +#endif + if (export_slicedata) { + BOOST_LOG_TRIVIAL(info) << "plate "<< index+1<< ":will export Slicing data to " << export_slice_data_dir; + std::string plate_dir = export_slice_data_dir+"/"+std::to_string(index+1); + bool with_space = (get_logging_level() >= 4)?true:false; + int ret = print->export_cached_data(plate_dir, with_space); + if (ret) { + BOOST_LOG_TRIVIAL(error) << "plate "<< index+1<< ": export Slicing data error, ret=" << ret; + export_slicedata_error = true; + if (fs::exists(plate_dir)) + fs::remove_all(plate_dir); + } + } + if (max_slicing_time_per_plate != 0) { + end_time = (long long)Slic3r::Utils::get_current_time_utc(); + long long time_cost = end_time - start_time; + if (time_cost > max_slicing_time_per_plate) { + BOOST_LOG_TRIVIAL(error) << boost::format("plate %1%'s slice time %2% exceeds the limit %3%, return error.") + %(index+1) %time_cost %max_slicing_time_per_plate; + flush_and_exit(CLI_SLICING_TIME_EXCEEDS_LIMIT); + } + } + } catch (const std::exception &ex) { + BOOST_LOG_TRIVIAL(error) << "found slicing or export error for partplate "< thumbnails; + std::vector thumbnails, top_thumbnails, pick_thumbnails; std::vector plate_bboxes; PlateDataPtrs plate_data_list; partplate_list.store_to_3mf_structure(plate_data_list); @@ -2058,12 +2202,15 @@ int CLI::run(int argc, char **argv) #if defined(__linux__) || defined(__LINUX__) if (g_cli_callback_mgr.is_started()) { - PrintBase::SlicingStatus slicing_status{91, "Generate thumbnails"}; + PrintBase::SlicingStatus slicing_status{94, "Generate thumbnails"}; cli_status_callback(slicing_status); } #endif - bool need_regenerate_thumbnail = oriented_or_arranged; + bool need_regenerate_thumbnail = oriented_or_arranged || regenerate_thumbnails; + bool need_regenerate_top_thumbnail = oriented_or_arranged || regenerate_thumbnails; + bool need_create_thumbnail_group = false, need_create_top_group = false; + // get type and color for platedata auto* filament_types = dynamic_cast(m_print_config.option("filament_type")); const ConfigOptionStrings* filament_color = dynamic_cast(m_print_config.option("filament_colour")); @@ -2071,27 +2218,59 @@ int CLI::run(int argc, char **argv) for (int i = 0; i < plate_data_list.size(); i++) { PlateData *plate_data = plate_data_list[i]; + bool skip_this_plate = ((plate_to_slice != 0) && (plate_to_slice != (i + 1)))?true:false; + for (auto it = plate_data->slice_filaments_info.begin(); it != plate_data->slice_filaments_info.end(); it++) { - it->type = filament_types?filament_types->get_at(it->id):"PLA"; - it->color = filament_color?filament_color->get_at(it->id):"#FFFFFF"; //it->filament_id = filament_id?filament_id->get_at(it->id):"unknown"; + std::string display_filament_type; + it->type = m_print_config.get_filament_type(display_filament_type, it->id); + it->color = filament_color ? filament_color->get_at(it->id) : "#FFFFFF"; } if (!plate_data->plate_thumbnail.is_valid()) { - if (!oriented_or_arranged && plate_data_src.size() > i) + if (!oriented_or_arranged && !regenerate_thumbnails && plate_data_src.size() > i) plate_data->thumbnail_file = plate_data_src[i]->thumbnail_file; BOOST_LOG_TRIVIAL(info) << boost::format("thumbnails stage: plate %1%'s thumbnail data is invalid, check the file %2% exist or not")%(i+1) %plate_data->thumbnail_file; if (plate_data->thumbnail_file.empty() || (!boost::filesystem::exists(plate_data->thumbnail_file))) { BOOST_LOG_TRIVIAL(info) << boost::format("thumbnails stage: plate %1%'s thumbnail file also not there, need to regenerate")%(i+1); - need_regenerate_thumbnail = true; + if (!skip_this_plate) { + need_regenerate_thumbnail = true; + need_create_thumbnail_group = true; + } } else { BOOST_LOG_TRIVIAL(info) << boost::format("thumbnails stage: plate %1%'s thumbnail file exists, no need to regenerate")%(i+1); } } + else { + if (regenerate_thumbnails) + plate_data->plate_thumbnail.reset(); + + if (!skip_this_plate) { + need_create_thumbnail_group = true; + } + } + + if (plate_data->top_file.empty() || plate_data->pick_file.empty()) { + if (!regenerate_thumbnails && (plate_data_src.size() > i)) { + plate_data->top_file = plate_data_src[i]->top_file; + plate_data->pick_file = plate_data_src[i]->pick_file; + } + if (plate_data->top_file.empty()|| plate_data->pick_file.empty() + || (!boost::filesystem::exists(plate_data->top_file)) || (!boost::filesystem::exists(plate_data->pick_file))) { + BOOST_LOG_TRIVIAL(info) << boost::format("thumbnails stage: plate %1%'s top_file %2% also not there, need to regenerate")%(i+1)%plate_data->top_file; + if (!skip_this_plate) { + need_regenerate_top_thumbnail = true; + need_create_top_group = true; + } + } + else { + BOOST_LOG_TRIVIAL(info) << boost::format("thumbnails stage: plate %1%'s top_thumbnail file exists, no need to regenerate")%(i+1); + } + } } - if (need_regenerate_thumbnail) { + if (need_regenerate_thumbnail || need_regenerate_top_thumbnail) { std::vector colors; if (filament_color) { colors= filament_color->vserialize(); @@ -2147,89 +2326,201 @@ int CLI::run(int argc, char **argv) else glfwMakeContextCurrent(window); } - bool opengl_valid = opengl_mgr.init_gl(); - if (!opengl_valid) { - BOOST_LOG_TRIVIAL(error) << "init opengl failed! skip thumbnail generating" << std::endl; - } - else { - BOOST_LOG_TRIVIAL(info) << "glewInit Sucess." << std::endl; - GLVolumeCollection glvolume_collection; - Model &model = m_models[0]; - int extruder_id = 1; - for (unsigned int obj_idx = 0; obj_idx < (unsigned int)model.objects.size(); ++ obj_idx) { - const ModelObject &model_object = *model.objects[obj_idx]; - const ConfigOption* option = model_object.config.option("extruder"); - if (option) - extruder_id = (dynamic_cast(option))->getInt(); - for (int volume_idx = 0; volume_idx < (int)model_object.volumes.size(); ++ volume_idx) { - const ModelVolume &model_volume = *model_object.volumes[volume_idx]; - option = model_volume.config.option("extruder"); - if (option) extruder_id = (dynamic_cast(option))->getInt(); - //if (!model_volume.is_model_part()) - // continue; - for (int instance_idx = 0; instance_idx < (int)model_object.instances.size(); ++ instance_idx) { - const ModelInstance &model_instance = *model_object.instances[instance_idx]; - glvolume_collection.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, "volume", true); - //glvolume_collection.volumes.back()->geometry_id = key.geometry_id; - std::string color = filament_color?filament_color->get_at(extruder_id - 1):"#00FF00"; - unsigned char rgb_color[3] = {}; - Slic3r::GUI::BitmapCache::parse_color(color, rgb_color); - glvolume_collection.volumes.back()->set_render_color( float(rgb_color[0]) / 255.f, float(rgb_color[1]) / 255.f, float(rgb_color[2]) / 255.f, 1.f); - - std::array new_color; - new_color[0] = float(rgb_color[0]) / 255.f; - new_color[1] = float(rgb_color[1]) / 255.f; - new_color[2] = float(rgb_color[2]) / 255.f; - new_color[3] = 1.f; - glvolume_collection.volumes.back()->set_color(new_color); - } - } - } - - ThumbnailsParams thumbnail_params; - GLShaderProgram* shader = opengl_mgr.get_shader("gouraud_light"); - if (!shader) { - BOOST_LOG_TRIVIAL(error) << boost::format("can not get shader for rendering thumbnail"); + //opengl manager related logic + { + Slic3r::GUI::OpenGLManager opengl_mgr; + bool opengl_valid = opengl_mgr.init_gl(); + if (!opengl_valid) { + BOOST_LOG_TRIVIAL(error) << "init opengl failed! skip thumbnail generating" << std::endl; } else { - for (int i = 0; i < partplate_list.get_plate_count(); i++) { - Slic3r::GUI::PartPlate *part_plate = partplate_list.get_plate(i); - PlateData *plate_data = plate_data_list[i]; - if (plate_data->plate_thumbnail.is_valid()) { - thumbnails.push_back(&plate_data->plate_thumbnail); - BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% has a valid thumbnail, width %2%, height %3%, directly using it")%(i+1) %plate_data->plate_thumbnail.width %plate_data->plate_thumbnail.height; - continue; - } - BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%'s thumbnail, need to regenerate")%(i+1); - ThumbnailData * thumbnail_data = new ThumbnailData(); - unsigned int thumbnail_width = 256, thumbnail_height = 256; - const ThumbnailsParams thumbnail_params = {{}, false, true, true, true, i}; + BOOST_LOG_TRIVIAL(info) << "glewInit Sucess." << std::endl; + GLVolumeCollection glvolume_collection; + Model &model = m_models[0]; + int extruder_id = 1; + for (unsigned int obj_idx = 0; obj_idx < (unsigned int)model.objects.size(); ++ obj_idx) { + const ModelObject &model_object = *model.objects[obj_idx]; + const ConfigOption* option = model_object.config.option("extruder"); + if (option) + extruder_id = (dynamic_cast(option))->getInt(); + for (int volume_idx = 0; volume_idx < (int)model_object.volumes.size(); ++ volume_idx) { + const ModelVolume &model_volume = *model_object.volumes[volume_idx]; + option = model_volume.config.option("extruder"); + if (option) extruder_id = (dynamic_cast(option))->getInt(); + //if (!model_volume.is_model_part()) + // continue; + for (int instance_idx = 0; instance_idx < (int)model_object.instances.size(); ++ instance_idx) { + const ModelInstance &model_instance = *model_object.instances[instance_idx]; + glvolume_collection.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, "volume", true, false, true); + //glvolume_collection.volumes.back()->geometry_id = key.geometry_id; + std::string color = filament_color?filament_color->get_at(extruder_id - 1):"#00FF00"; - switch (Slic3r::GUI::OpenGLManager::get_framebuffers_type()) - { - case Slic3r::GUI::OpenGLManager::EFramebufferType::Arb: - { - BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: ARB"); - Slic3r::GUI::GLCanvas3D::render_thumbnail_framebuffer(*thumbnail_data, - thumbnail_width, thumbnail_height, thumbnail_params, - partplate_list, model.objects, glvolume_collection, colors_out, shader, Slic3r::GUI::Camera::EType::Ortho); - break; - } - case Slic3r::GUI::OpenGLManager::EFramebufferType::Ext: - { - BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: EXT"); - Slic3r::GUI::GLCanvas3D::render_thumbnail_framebuffer_ext(*thumbnail_data, - thumbnail_width, thumbnail_height, thumbnail_params, - partplate_list, model.objects, glvolume_collection, colors_out, shader, Slic3r::GUI::Camera::EType::Ortho); - break; - } - default: - BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: unknown"); - break; + unsigned char rgb_color[3] = {}; + Slic3r::GUI::BitmapCache::parse_color(color, rgb_color); + glvolume_collection.volumes.back()->set_render_color( float(rgb_color[0]) / 255.f, float(rgb_color[1]) / 255.f, float(rgb_color[2]) / 255.f, 1.f); + + std::array new_color; + new_color[0] = float(rgb_color[0]) / 255.f; + new_color[1] = float(rgb_color[1]) / 255.f; + new_color[2] = float(rgb_color[2]) / 255.f; + new_color[3] = 1.f; + glvolume_collection.volumes.back()->set_color(new_color); + glvolume_collection.volumes.back()->printable = model_instance.printable; + } + } + } + + ThumbnailsParams thumbnail_params; + GLShaderProgram* shader = opengl_mgr.get_shader("thumbnail"); + if (!shader) { + BOOST_LOG_TRIVIAL(error) << boost::format("can not get shader for rendering thumbnail"); + } + else { + for (int i = 0; i < partplate_list.get_plate_count(); i++) { + Slic3r::GUI::PartPlate *part_plate = partplate_list.get_plate(i); + PlateData *plate_data = plate_data_list[i]; + if (plate_data->plate_thumbnail.is_valid()) { + if ((plate_to_slice != 0) && (plate_to_slice != (i + 1))) { + BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: regenerate thumbnail, reset plate %2%'s thumbnail.")%__LINE__%(i+1); + plate_data->plate_thumbnail.reset(); + } + else + BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% has a valid thumbnail, width %2%, height %3%, directly using it")%(i+1) %plate_data->plate_thumbnail.width %plate_data->plate_thumbnail.height; + } + else if (!plate_data->thumbnail_file.empty() && (boost::filesystem::exists(plate_data->thumbnail_file))) + { + if ((plate_to_slice != 0) && (plate_to_slice != (i + 1))) { + BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: regenerate thumbnail, clear plate %2%'s thumbnail file path to empty.")%__LINE__%(i+1); + plate_data->thumbnail_file.clear(); + } + else + BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% has a valid thumbnail %2% extracted from 3mf, directly using it")%(i+1) %plate_data->thumbnail_file; + } + else { + ThumbnailData* thumbnail_data = &plate_data->plate_thumbnail; + + if ((plate_to_slice != 0) && (plate_to_slice != (i + 1))) { + BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: regenerate thumbnail, Skip plate %2%.")%__LINE__%(i+1); + } + else { + unsigned int thumbnail_width = 512, thumbnail_height = 512; + const ThumbnailsParams thumbnail_params = {{}, false, true, true, true, i}; + + BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%'s thumbnail, need to regenerate")%(i+1); + switch (Slic3r::GUI::OpenGLManager::get_framebuffers_type()) + { + case Slic3r::GUI::OpenGLManager::EFramebufferType::Arb: + { + BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: ARB"); + Slic3r::GUI::GLCanvas3D::render_thumbnail_framebuffer(*thumbnail_data, + thumbnail_width, thumbnail_height, thumbnail_params, + partplate_list, model.objects, glvolume_collection, colors_out, shader, Slic3r::GUI::Camera::EType::Ortho); + break; + } + case Slic3r::GUI::OpenGLManager::EFramebufferType::Ext: + { + BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: EXT"); + Slic3r::GUI::GLCanvas3D::render_thumbnail_framebuffer_ext(*thumbnail_data, + thumbnail_width, thumbnail_height, thumbnail_params, + partplate_list, model.objects, glvolume_collection, colors_out, shader, Slic3r::GUI::Camera::EType::Ortho); + break; + } + default: + BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: unknown"); + break; + } + BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%'s thumbnail,finished rendering")%(i+1); + } + } + if (need_create_thumbnail_group) { + thumbnails.push_back(&plate_data->plate_thumbnail); + BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%: add thumbnail data into group")%(i+1); + } + + //top thumbnails + /*if (part_plate->top_thumbnail_data.is_valid() && part_plate->pick_thumbnail_data.is_valid()) { + if ((plate_to_slice != 0) && (plate_to_slice != (i + 1))) { + BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: regenerate thumbnail, reset plate %2%'s top/pick thumbnail.")%__LINE__%(i+1); + part_plate->top_thumbnail_data.reset(); + part_plate->pick_thumbnail_data.reset(); + plate_data->top_file.clear(); + plate_data->pick_file.clear(); + } + else { + plate_data->top_file = "valid_top"; + plate_data->pick_file = "valid_pick"; + BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% has a valid top/pick thumbnail data, directly using it")%(i+1); + } + } + else*/ + if ((!plate_data->top_file.empty() && (boost::filesystem::exists(plate_data->top_file))) + &&(!plate_data->pick_file.empty() && (boost::filesystem::exists(plate_data->pick_file)))) + { + if ((plate_to_slice != 0) && (plate_to_slice != (i + 1))) { + BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: regenerate thumbnail, clear plate %2%'s top/pick thumbnail file path to empty.")%__LINE__%(i+1); + plate_data->top_file.clear(); + plate_data->pick_file.clear(); + } + else + BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% has valid top/pick thumbnail extracted from 3mf, directly using it")%(i+1); + } + else{ + ThumbnailData* top_thumbnail = &part_plate->top_thumbnail_data; + ThumbnailData* picking_thumbnail = &part_plate->pick_thumbnail_data; + if ((plate_to_slice != 0) && (plate_to_slice != (i + 1))) { + BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: regenerate thumbnail, Skip plate %2%.")%__LINE__%(i+1); + part_plate->top_thumbnail_data.reset(); + part_plate->pick_thumbnail_data.reset(); + plate_data->top_file.clear(); + plate_data->pick_file.clear(); + } + else { + unsigned int thumbnail_width = 512, thumbnail_height = 512; + const ThumbnailsParams thumbnail_params = { {}, false, true, false, true, i }; + + BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%'s top/pick thumbnail missed, need to regenerate")%(i+1); + + switch (Slic3r::GUI::OpenGLManager::get_framebuffers_type()) + { + case Slic3r::GUI::OpenGLManager::EFramebufferType::Arb: + { + BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: ARB"); + Slic3r::GUI::GLCanvas3D::render_thumbnail_framebuffer(*top_thumbnail, + thumbnail_width, thumbnail_height, thumbnail_params, + partplate_list, model.objects, glvolume_collection, colors_out, shader, Slic3r::GUI::Camera::EType::Ortho, true, false); + Slic3r::GUI::GLCanvas3D::render_thumbnail_framebuffer(*picking_thumbnail, + thumbnail_width, thumbnail_height, thumbnail_params, + partplate_list, model.objects, glvolume_collection, colors_out, shader, Slic3r::GUI::Camera::EType::Ortho, true, true); + break; + } + case Slic3r::GUI::OpenGLManager::EFramebufferType::Ext: + { + BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: EXT"); + Slic3r::GUI::GLCanvas3D::render_thumbnail_framebuffer_ext(*top_thumbnail, + thumbnail_width, thumbnail_height, thumbnail_params, + partplate_list, model.objects, glvolume_collection, colors_out, shader, Slic3r::GUI::Camera::EType::Ortho, true, false); + Slic3r::GUI::GLCanvas3D::render_thumbnail_framebuffer_ext(*picking_thumbnail, + thumbnail_width, thumbnail_height, thumbnail_params, + partplate_list, model.objects, glvolume_collection, colors_out, shader, Slic3r::GUI::Camera::EType::Ortho, true, true); + break; + } + default: + BOOST_LOG_TRIVIAL(info) << boost::format("framebuffer_type: unknown"); + break; + } + plate_data->top_file = "valid_top"; + plate_data->pick_file = "valid_pick"; + BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%'s top_thumbnail,finished rendering")%(i+1); + } + } + + if (need_create_top_group) { + top_thumbnails.push_back(&part_plate->top_thumbnail_data); + pick_thumbnails.push_back(&part_plate->pick_thumbnail_data); + BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%: add thumbnail data for top and pick into group")%(i+1); + } } - thumbnails.push_back(thumbnail_data); - BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%'s thumbnail,finished rendering")%(i+1); } } } @@ -2237,17 +2528,42 @@ int CLI::run(int argc, char **argv) glfwTerminate(); } else { + BOOST_LOG_TRIVIAL(info) << boost::format("use previous thumbnails, no need to regenerate"); for (int i = 0; i < partplate_list.get_plate_count(); i++) { PlateData *plate_data = plate_data_list[i]; - if (plate_data->plate_thumbnail.is_valid()) { + bool skip_this_plate = ((plate_to_slice != 0) && (plate_to_slice != (i + 1)))?true:false; + Slic3r::GUI::PartPlate *part_plate = partplate_list.get_plate(i); + + if (skip_this_plate) { + BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%'s all the thumbnails skipped, reset here")%(i+1); + plate_data->plate_thumbnail.reset(); + plate_data->thumbnail_file.clear(); + part_plate->top_thumbnail_data.reset(); + part_plate->pick_thumbnail_data.reset(); + plate_data->top_file.clear(); + plate_data->pick_file.clear(); + } + + if (need_create_thumbnail_group) { thumbnails.push_back(&plate_data->plate_thumbnail); - BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% has a valid thumbnail data, width %2%, height %3%, directly using it")%(i+1) %plate_data->plate_thumbnail.width %plate_data->plate_thumbnail.height; + BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%: add thumbnail data into group")%(i+1); + } + + if (need_create_top_group) { + top_thumbnails.push_back(&part_plate->top_thumbnail_data); + pick_thumbnails.push_back(&part_plate->pick_thumbnail_data); + BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%: add thumbnail data for top and pick into group")%(i+1); } } } //generate first layer bboxes for (int i = 0; i < partplate_list.get_plate_count(); i++) { + if ((plate_to_slice != 0) && (plate_to_slice != (i + 1))) { + BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: generate bbox, Skip plate %2%.")%__LINE__%(i+1); + plate_bboxes.push_back(new PlateBBoxData()); + continue; + } Slic3r::GUI::PartPlate *part_plate = partplate_list.get_plate(i); //render calibration thumbnail if (!part_plate->get_slice_result() || !part_plate->is_slice_result_valid()) { @@ -2289,9 +2605,36 @@ int CLI::run(int argc, char **argv) PlateBBoxData* plate_bbox = new PlateBBoxData(); std::vector& id_bboxes = plate_bbox->bbox_objs; BoundingBoxf bbox_all; - auto seq_print = m_print_config.option>("print_sequence"); - if ( seq_print && (seq_print->value == PrintSequence::ByObject)) + PrintSequence curr_plate_seq = part_plate->get_print_seq(); + if (curr_plate_seq == PrintSequence::ByDefault) { + auto seq_print = m_print_config.option>("print_sequence"); + if (seq_print && (seq_print->value == PrintSequence::ByObject)) { + BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% print by object, set from global")%(i+1); + plate_bbox->is_seq_print = true; + } + } + else if (curr_plate_seq == PrintSequence::ByObject) { + BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% print by object, set from plate self")%(i+1); plate_bbox->is_seq_print = true; + } + plate_bbox->first_extruder = print->get_tool_ordering().first_extruder(); + //bed type; + BedType plate_bed_type = part_plate->get_bed_type(); + if (plate_bed_type == btDefault) { + auto cur_bed_type = m_print_config.option>("curr_bed_type"); + if (cur_bed_type) { + BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% bed type: %2%, set from global")%(i+1) %cur_bed_type->serialize(); + plate_bbox->bed_type = bed_type_to_gcode_string(cur_bed_type->value); + } + } + else { + BOOST_LOG_TRIVIAL(info) << boost::format("plate %1% bed type: %2%, set from plate self")%(i+1) %plate_bed_type; + plate_bbox->bed_type = bed_type_to_gcode_string(plate_bed_type); + } + // get nozzle diameter + auto opt_nozzle_diameters = m_print_config.option("nozzle_diameter"); + if (opt_nozzle_diameters != nullptr) + plate_bbox->nozzle_diameter = float(opt_nozzle_diameters->get_at(plate_bbox->first_extruder)); auto objects = print->objects(); auto orig = part_plate->get_origin(); @@ -2328,26 +2671,37 @@ int CLI::run(int argc, char **argv) } } plate_bbox->bbox_all = { bbox_all.min.x(),bbox_all.min.y(),bbox_all.max.x(),bbox_all.max.y() }; + + PlateData *plate_data = plate_data_list[i]; + for (auto it = plate_data->slice_filaments_info.begin(); it != plate_data->slice_filaments_info.end(); it++) { + plate_bbox->filament_ids.push_back(it->id); + plate_bbox->filament_colors.push_back(it->color); + } plate_bboxes.push_back(plate_bbox); } #if defined(__linux__) || defined(__LINUX__) if (g_cli_callback_mgr.is_started()) { - PrintBase::SlicingStatus slicing_status{95, "Exporting 3mf"}; + PrintBase::SlicingStatus slicing_status{97, "Exporting 3mf"}; cli_status_callback(slicing_status); } #endif BOOST_LOG_TRIVIAL(info) << "will export 3mf to " << export_3mf_file << std::endl; - if (! this->export_project(&m_models[0], export_3mf_file, plate_data_list, project_presets, thumbnails, calibration_thumbnails, plate_bboxes, &m_print_config)) + if (! this->export_project(&m_models[0], export_3mf_file, plate_data_list, project_presets, thumbnails, top_thumbnails, pick_thumbnails, + calibration_thumbnails, plate_bboxes, &m_print_config)) { release_PlateData_list(plate_data_list); flush_and_exit(CLI_EXPORT_3MF_ERROR); } release_PlateData_list(plate_data_list); for (unsigned int i = 0; i < thumbnails.size(); i++) - delete thumbnails[i]; + thumbnails[i]->reset(); + for (unsigned int i = 0; i < top_thumbnails.size(); i++) + top_thumbnails[i]->reset(); + for (unsigned int i = 0; i < pick_thumbnails.size(); i++) + pick_thumbnails[i]->reset(); for (unsigned int i = 0; i < calibration_thumbnails.size(); i++) delete calibration_thumbnails[i]; @@ -2370,6 +2724,9 @@ int CLI::run(int argc, char **argv) g_cli_callback_mgr.stop(); #endif + for (Model &model : m_models) { + model.remove_backup_path_if_exist(); + } //BBS: flush logs BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", Finished" << std::endl; boost::nowide::cout.flush(); @@ -2523,7 +2880,8 @@ bool CLI::export_models(IO::ExportFormat format) //BBS: add export_project function bool CLI::export_project(Model *model, std::string& path, PlateDataPtrs &partplate_data, - std::vector& project_presets, std::vector& thumbnails, std::vector& calibration_thumbnails, std::vector& plate_bboxes, const DynamicPrintConfig* config) + std::vector& project_presets, std::vector& thumbnails, std::vector& top_thumbnails, std::vector& pick_thumbnails, + std::vector& calibration_thumbnails, std::vector& plate_bboxes, const DynamicPrintConfig* config) { //const std::string path = this->output_filepath(*model, IO::TMF); bool success = false; @@ -2535,9 +2893,11 @@ bool CLI::export_project(Model *model, std::string& path, PlateDataPtrs &partpla store_params.project_presets = project_presets; store_params.config = (DynamicPrintConfig*)config; store_params.thumbnail_data = thumbnails; + store_params.top_thumbnail_data = top_thumbnails; + store_params.pick_thumbnail_data = pick_thumbnails; store_params.calibration_thumbnail_data = calibration_thumbnails; store_params.id_bboxes = plate_bboxes; - store_params.strategy = SaveStrategy::Silence|SaveStrategy::WithGcode|SaveStrategy::SplitModel; + store_params.strategy = SaveStrategy::Silence|SaveStrategy::WithGcode|SaveStrategy::SplitModel|SaveStrategy::UseLoadedId; success = Slic3r::store_bbs_3mf(store_params); diff --git a/src/BambuStudio.hpp b/src/BambuStudio.hpp index f5c5e6b734..c7fc5ae3e1 100644 --- a/src/BambuStudio.hpp +++ b/src/BambuStudio.hpp @@ -39,7 +39,8 @@ private: bool export_models(IO::ExportFormat format); //BBS: add export_project function bool export_project(Model *model, std::string& path, PlateDataPtrs &partplate_data, std::vector& project_presets, - std::vector& thumbnails, std::vector& calibration_thumbnails, + std::vector& thumbnails, std::vector& top_thumbnails, std::vector& pick_thumbnails, + std::vector& calibration_thumbnails, std::vector& plate_bboxes, const DynamicPrintConfig* config); bool has_print_action() const { return m_config.opt_bool("export_gcode") || m_config.opt_bool("export_sla"); } diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h index 51aa772cdb..cdb6275cec 100644 --- a/src/imgui/imconfig.h +++ b/src/imgui/imconfig.h @@ -189,6 +189,10 @@ namespace ImGui const wchar_t TextSearchIcon = 0x0828; const wchar_t TextSearchCloseIcon = 0x0829; + const wchar_t ExpandBtn = 0x0830; + const wchar_t CollapseBtn = 0x0831; + const wchar_t RevertBtn = 0x0832; + // void MyFunction(const char* name, const MyMatrix44& v); } diff --git a/src/imgui/imgui_draw.cpp b/src/imgui/imgui_draw.cpp index 8dac5adbc4..913a551fa0 100644 --- a/src/imgui/imgui_draw.cpp +++ b/src/imgui/imgui_draw.cpp @@ -3815,7 +3815,7 @@ void ImGui::RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir d draw_list->AddTriangleFilled(center + a, center + b, center + c, col); } -void ImGui::BBLRenderArrow(ImDrawList *draw_list, ImVec2 pos, ImU32 col, ImGuiDir dir, float scale) +void ImGui::BBLRenderArrow(ImDrawList *draw_list, ImVec2 pos, ImU32 col, ImGuiDir dir, float thickness, float scale) { const float h = draw_list->_Data->FontSize * 1.00f; float r = h * 0.40f * scale; @@ -3841,8 +3841,8 @@ void ImGui::BBLRenderArrow(ImDrawList *draw_list, ImVec2 pos, ImU32 col, ImGuiDi case ImGuiDir_COUNT: IM_ASSERT(0); break; } //draw_list->AddTriangleFilled(center + a, center + b, center + c, col); - draw_list->AddLine(center + a, center + c,col); - draw_list->AddLine(center + a, center + b,col); + draw_list->AddLine(center + a, center + c, col, thickness); + draw_list->AddLine(center + a, center + b, col, thickness); } void ImGui::RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col) diff --git a/src/imgui/imgui_internal.h b/src/imgui/imgui_internal.h index a10b0ac0f0..70be29caa0 100644 --- a/src/imgui/imgui_internal.h +++ b/src/imgui/imgui_internal.h @@ -2528,7 +2528,7 @@ namespace ImGui // Render helpers (those functions don't access any ImGui state!) IMGUI_API void RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir dir, float scale = 1.0f); - IMGUI_API void BBLRenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir dir, float scale = 1.0f); + IMGUI_API void BBLRenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir dir, float thickness = 1.0f, float scale = 1.0f); IMGUI_API void RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col); IMGUI_API void RenderCheckMark(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float sz); IMGUI_API void RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow); diff --git a/src/libnest2d/include/libnest2d/common.hpp b/src/libnest2d/include/libnest2d/common.hpp index 9c6a052d23..8cb77d5a4f 100644 --- a/src/libnest2d/include/libnest2d/common.hpp +++ b/src/libnest2d/include/libnest2d/common.hpp @@ -11,7 +11,7 @@ #include #include -#define MAX_NUM_PLATES 50 +#define MAX_NUM_PLATES 36 #if defined(_MSC_VER) && _MSC_VER <= 1800 || __cplusplus < 201103L #define BP2D_NOEXCEPT diff --git a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp index 08afd130b5..ae0fcb36ec 100644 --- a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp +++ b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp @@ -602,6 +602,22 @@ private: return nfp::subtract({innerNfp}, nfps); } + Shapes calcnfp(const RawShape &sliding, const Shapes &stationarys, const Box &bed, Lvl) + { + using namespace nfp; + + Shapes nfps(stationarys.size()); + Item slidingItem(sliding); + slidingItem.transformedShape(); + __parallel::enumerate(stationarys.begin(), stationarys.end(), [&nfps, sliding, &slidingItem](const RawShape &stationary, size_t n) { + auto subnfp_r = noFitPolygon(stationary, sliding); + correctNfpPosition(subnfp_r, stationary, slidingItem); + nfps[n] = subnfp_r.first; + }); + + RawShape innerNfp = nfpInnerRectBed(bed, sliding).first; + return nfp::subtract({innerNfp}, nfps); + } template Shapes calcnfp(const Item &/*trsh*/, Level) @@ -702,18 +718,31 @@ private: }; } - if(items_.empty()) { + bool first_object = std::all_of(items_.begin(), items_.end(), [&](const Item &rawShape) { return rawShape.is_virt_object && !rawShape.is_wipe_tower; }); + + // item won't overlap with virtual objects if it's inside or touches NFP + auto overlapWithVirtObject = [&]() -> double { + if (items_.empty()) return 0; + nfps = calcnfp(item, binbb, Lvl()); + auto v = item.referenceVertex(); + for (const RawShape &nfp : nfps) { + if (sl::isInside(v, nfp) || sl::touches(v, nfp)) { return 0; } + } + return 1; + }; + + if (first_object) { setInitialPosition(item); auto best_tr = item.translation(); auto best_rot = item.rotation(); - best_overfit = overfit(item.transformedShape(), bin_); + best_overfit = overfit(item.transformedShape(), bin_) + overlapWithVirtObject(); for(auto rot : config_.rotations) { item.translation(initial_tr); item.rotation(initial_rot + rot); setInitialPosition(item); double of = 0.; - if ((of = overfit(item.transformedShape(), bin_)) < best_overfit) { + if ((of = overfit(item.transformedShape(), bin_)) + overlapWithVirtObject() < best_overfit) { best_overfit = of; best_tr = item.translation(); best_rot = item.rotation(); @@ -725,7 +754,8 @@ private: global_score = 0.2; item.rotation(best_rot); item.translation(best_tr); - } else { + } + if (can_pack == false) { Pile merged_pile = merged_pile_; @@ -1035,27 +1065,9 @@ private: if (!item.is_virt_object) bb = sl::boundingBox(item.boundingBox(), bb); - // if move to center is infeasible, move to topright corner instead - auto alignment = config_.alignment; - if (!config_.m_excluded_regions.empty() && alignment== Config::Alignment::CENTER) { - Box bb2 = bb; - auto d = bbin.center() - bb2.center(); - d.x() = std::max(d.x(), 0); - d.y() = std::max(d.y(), 0); - bb2.minCorner() += d; - bb2.maxCorner() += d; - for (auto& region : config_.m_excluded_regions) { - auto region_bb = region.boundingBox(); - if (bb2.intersection(region_bb).area()>0) { - alignment = Config::Alignment::TOP_RIGHT; - break; - } - } - } - Vertex ci, cb; - switch(alignment) { + switch(config_.alignment) { case Config::Alignment::CENTER: { ci = bb.center(); cb = bbin.center(); @@ -1086,13 +1098,44 @@ private: auto d = cb - ci; - // BBS TODO we assume the exclude region contains bottom left corner. If not, change the code below - if (!config_.m_excluded_regions.empty()) { // do not move to left to much to avoid clash with excluded regions - if (d.x() < 0) { - d.x() = 0;// std::max(long(d.x()), long(bbin.maxCorner().x() - bb.maxCorner().x())); + // BBS make sure the item won't clash with excluded regions + // do we have wipe tower after arranging? + std::set extruders; + for (const Item& item : items_) { + if (!item.is_virt_object) { extruders.insert(item.extrude_ids.begin(), item.extrude_ids.end()); } + } + bool need_wipe_tower = extruders.size() > 1; + + std::vector objs,excludes; + for (const Item &item : items_) { + if (item.isFixed()) continue; + objs.push_back(item.transformedShape()); + } + if (objs.empty()) + return; + { // find a best position inside NFP of fixed items (excluded regions), so the center of pile is cloest to bed center + RawShape objs_convex_hull = sl::convexHull(objs); + for (const Item &item : config_.m_excluded_regions) { excludes.push_back(item.transformedShape()); } + for (const Item &item : items_) { + if (item.isFixed()) { + if (!(item.is_wipe_tower && !need_wipe_tower)) + excludes.push_back(item.transformedShape()); + } } - if (d.y() < 0) { - d.y() = 0;// std::max(long(d.y()), long(bbin.maxCorner().y() - bb.maxCorner().y())); + + auto nfps = calcnfp(objs_convex_hull, excludes, bbin, Lvl()); + if (nfps.empty()) { + return; + } + Item objs_convex_hull_item(objs_convex_hull); + Vertex objs_convex_hull_ref = objs_convex_hull_item.referenceVertex(); + Vertex diff = objs_convex_hull_ref - sl::boundingBox(objs_convex_hull).center(); + Vertex ref_aligned = cb + diff; // reference point when pile center aligned with bed center + bool ref_aligned_is_ok = std::any_of(nfps.begin(), nfps.end(), [&ref_aligned](auto& nfp) {return sl::isInside(ref_aligned, nfp); }); + if (!ref_aligned_is_ok) { + // ref_aligned is not good, then find a nearest point on nfp boundary + Vertex ref_projected = projection_onto(nfps, ref_aligned); + d += (ref_projected - ref_aligned); } } for(Item& item : items_) @@ -1104,7 +1147,10 @@ private: Box bb = item.boundingBox(); Vertex ci, cb; - auto bbin = sl::boundingBox(bin_); + Box bbin = sl::boundingBox(bin_); + Vertex shrink(10, 10); + bbin.maxCorner() -= shrink; + bbin.minCorner() += shrink; switch(config_.starting_point) { case Config::Alignment::CENTER: { diff --git a/src/libslic3r/AABBTreeIndirect.hpp b/src/libslic3r/AABBTreeIndirect.hpp index 3958c95f97..6d6479508f 100644 --- a/src/libslic3r/AABBTreeIndirect.hpp +++ b/src/libslic3r/AABBTreeIndirect.hpp @@ -13,6 +13,7 @@ #include +#include "BoundingBox.hpp" #include "Utils.hpp" // for next_highest_power_of_2() // Definition of the ray intersection hit structure. @@ -83,6 +84,13 @@ public: // to split around. template void build(std::vector &&input) + { + this->build_modify_input(input); + input.clear(); + } + + template + void build_modify_input(std::vector &input) { if (input.empty()) clear(); @@ -91,7 +99,6 @@ public: m_nodes.assign(next_highest_power_of_2(input.size()) * 2 - 1, Node()); build_recursive(input, 0, 0, input.size() - 1); } - input.clear(); } const std::vector& nodes() const { return m_nodes; } @@ -211,6 +218,23 @@ using Tree3f = Tree<3, float>; using Tree2d = Tree<2, double>; using Tree3d = Tree<3, double>; +// Wrap a 2D Slic3r own BoundingBox to be passed to Tree::build() and similar +// to build an AABBTree over coord_t 2D bounding boxes. +class BoundingBoxWrapper { +public: + using BoundingBox = Eigen::AlignedBox; + BoundingBoxWrapper(const size_t idx, const Slic3r::BoundingBox &bbox) : + m_idx(idx), + // Inflate the bounding box a bit to account for numerical issues. + m_bbox(bbox.min - Point(SCALED_EPSILON, SCALED_EPSILON), bbox.max + Point(SCALED_EPSILON, SCALED_EPSILON)) {} + size_t idx() const { return m_idx; } + const BoundingBox& bbox() const { return m_bbox; } + Point centroid() const { return ((m_bbox.min().cast() + m_bbox.max().cast()) / 2).cast(); } +private: + size_t m_idx; + BoundingBox m_bbox; +}; + namespace detail { template struct RayIntersector { @@ -513,7 +537,7 @@ namespace detail { const VectorType origin; inline VectorType closest_point_to_origin(size_t primitive_index, - ScalarType& squared_distance){ + ScalarType& squared_distance) const { const auto &triangle = this->faces[primitive_index]; VectorType closest_point = closest_point_to_triangle(origin, this->vertices[triangle(0)].template cast(), @@ -895,48 +919,54 @@ struct Intersecting> { template auto intersecting(const G &g) { return Intersecting{g}; } -template struct Containing {}; +template struct Within {}; // Intersection predicate specialization for box-box intersections template -struct Containing> { +struct Within> { Eigen::AlignedBox box; - Containing(const Eigen::AlignedBox &bb): box{bb} {} + Within(const Eigen::AlignedBox &bb): box{bb} {} bool operator() (const typename Tree::Node &node) const { - return box.contains(node.bbox); + return node.is_leaf() ? box.contains(node.bbox) : box.intersects(node.bbox); } }; -template auto containing(const G &g) { return Containing{g}; } +template auto within(const G &g) { return Within{g}; } namespace detail { +// Returns true in case traversal should continue, +// returns false if traversal should stop (for example if the first hit was found). template -void traverse_recurse(const Tree &tree, +bool traverse_recurse(const Tree &tree, size_t idx, Pred && pred, Fn && callback) { assert(tree.node(idx).is_valid()); - if (!pred(tree.node(idx))) return; + if (!pred(tree.node(idx))) + // Continue traversal. + return true; if (tree.node(idx).is_leaf()) { - callback(tree.node(idx).idx); + // Callback returns true to continue traversal, false to stop traversal. + return callback(tree.node(idx)); } else { // call this with left and right node idx: - auto trv = [&](size_t idx) { - traverse_recurse(tree, idx, std::forward(pred), - std::forward(callback)); + auto trv = [&](size_t idx) -> bool { + return traverse_recurse(tree, idx, std::forward(pred), + std::forward(callback)); }; // Left / right child node index. - trv(Tree::left_child_idx(idx)); - trv(Tree::right_child_idx(idx)); + // Returns true if both children allow the traversal to continue. + return trv(Tree::left_child_idx(idx)) && + trv(Tree::right_child_idx(idx)); } } @@ -946,6 +976,7 @@ void traverse_recurse(const Tree &tree, // traverse(tree, intersecting(QueryBox), [](size_t face_idx) { // /* ... */ // }); +// Callback shall return true to continue traversal, false if it wants to stop traversal, for example if it found the answer. template void traverse(const Tree &tree, Predicate &&pred, Fn &&callback) { diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index c56a56192f..f7fcf0d95f 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -170,6 +170,9 @@ void AppConfig::set_defaults() set_bool("reverse_mouse_wheel_zoom", false); #endif + if (get("zoom_to_mouse").empty()) + set_bool("zoom_to_mouse", false); + //#ifdef SUPPORT_SHOW_HINTS if (get("show_hints").empty()) set_bool("show_hints", true); @@ -287,9 +290,17 @@ void AppConfig::set_defaults() if (get("mouse_wheel").empty()) { set("mouse_wheel", "0"); } + + if (get("max_recent_count").empty()) { + set("max_recent_count", "18"); + } - if (get("backup_switch").empty()) { - set_bool("backup_switch", false); + if (get("sync_system_preset").empty()) { + set_bool("sync_system_preset", true); + } + + if (get("backup_switch").empty() || get("version") < "01.06.00.00") { + set_bool("backup_switch", true); } if (get("backup_interval").empty()) { @@ -314,6 +325,16 @@ void AppConfig::set_defaults() set_str("presets", "filament_colors", "#F2754E"); } + if (get("print", "bed_leveling").empty()) { + set_str("print", "bed_leveling", "1"); + } + if (get("print", "flow_cali").empty()) { + set_str("print", "flow_cali", "1"); + } + if (get("print", "timelapse").empty()) { + set_str("print", "timelapse", "1"); + } + // Remove legacy window positions/sizes erase("app", "main_frame_maximized"); erase("app", "main_frame_pos"); diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index 2eb90dfe6b..eeb385676a 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -85,7 +85,7 @@ const double BIG_ITEM_TRESHOLD = 0.02; template void fill_config(PConf& pcfg, const ArrangeParams ¶ms) { - if (params.is_seq_print || params.excluded_regions.empty()==false) { + if (params.is_seq_print) { // Align the arranged pile into the center of the bin pcfg.alignment = PConf::Alignment::CENTER; // Start placing the items from the center of the print bed @@ -95,7 +95,7 @@ void fill_config(PConf& pcfg, const ArrangeParams ¶ms) { // Align the arranged pile into the center of the bin pcfg.alignment = PConf::Alignment::CENTER; // Start placing the items from the center of the print bed - pcfg.starting_point = PConf::Alignment::CENTER; + pcfg.starting_point = PConf::Alignment::TOP_RIGHT; } // Try 4 angles (45 degree step) and find the one with min cost @@ -453,19 +453,23 @@ protected: std::set extruder_ids; int non_virt_cnt = 0; + std::set first_object_extruder_ids; for (int i = 0; i < m_items.size(); i++) { Item& p = m_items[i]; if (p.is_virt_object) continue; extruder_ids.insert(p.extrude_ids.begin(),p.extrude_ids.end()); non_virt_cnt++; + if (non_virt_cnt == 1) { first_object_extruder_ids.insert(p.extrude_ids.begin(), p.extrude_ids.end()); } } extruder_ids.insert(item.extrude_ids.begin(),item.extrude_ids.end()); // add a large cost if not multi materials on same plate is not allowed if (!params.allow_multi_materials_on_same_plate) { + bool first_object = non_virt_cnt == 0; + bool same_color_with_first_object = std::all_of(item.extrude_ids.begin(), item.extrude_ids.end(), + [&](int color) { return first_object_extruder_ids.find(color) != first_object_extruder_ids.end(); }); // non_virt_cnt==0 means it's the first object, which can be multi-color - if (extruder_ids.size() > 1 && non_virt_cnt > 0) - score += LARGE_COST_TO_REJECT * 1.1; + if (!(first_object || same_color_with_first_object)) score += LARGE_COST_TO_REJECT * 1.3; } // for layered printing, we want extruder change as few as possible // this has very weak effect, CAN NOT use a large weight @@ -541,11 +545,11 @@ public: auto binbb = sl::boundingBox(m_bin); // BBS: excluded region (virtual object but not wipe tower) should not affect final alignment - bool all_is_excluded_region = std::all_of(items.begin(), items.end(), [](Item &itm) { return itm.is_virt_object && !itm.is_wipe_tower; }); - if (!all_is_excluded_region) - cfg.alignment = PConfig::Alignment::DONT_ALIGN; - else - cfg.alignment = PConfig::Alignment::CENTER; + //bool all_is_excluded_region = std::all_of(items.begin(), items.end(), [](Item &itm) { return itm.is_virt_object && !itm.is_wipe_tower; }); + //if (!all_is_excluded_region) + // cfg.alignment = PConfig::Alignment::DONT_ALIGN; + //else + // cfg.alignment = PConfig::Alignment::CENTER; auto starting_point = cfg.starting_point == PConfig::Alignment::BOTTOM_LEFT ? binbb.minCorner() : binbb.center(); // if we have wipe tower, items should be arranged around wipe tower diff --git a/src/libslic3r/BridgeDetector.cpp b/src/libslic3r/BridgeDetector.cpp index a60f45f11a..09a2c2dc3b 100644 --- a/src/libslic3r/BridgeDetector.cpp +++ b/src/libslic3r/BridgeDetector.cpp @@ -160,7 +160,8 @@ bool BridgeDetector::detect_angle(double bridge_direction_override) // if any other direction is within extrusion width of coverage, prefer it if shorter // TODO: There are two options here - within width of the angle with most coverage, or within width of the currently perferred? size_t i_best = 0; - for (size_t i = 1; i < candidates.size() && abs(candidates[i_best].archored_percent - candidates[i].archored_percent) < EPSILON; ++ i) +// for (size_t i = 1; i < candidates.size() && abs(candidates[i_best].archored_percent - candidates[i].archored_percent) < EPSILON; ++ i) + for (size_t i = 1; i < candidates.size() && candidates[i_best].coverage - candidates[i].coverage < this->spacing; ++ i) if (candidates[i].max_length < candidates[i_best].max_length) i_best = i; diff --git a/src/libslic3r/BridgeDetector.hpp b/src/libslic3r/BridgeDetector.hpp index d422949cd7..064e40dd51 100644 --- a/src/libslic3r/BridgeDetector.hpp +++ b/src/libslic3r/BridgeDetector.hpp @@ -1,6 +1,12 @@ #ifndef slic3r_BridgeDetector_hpp_ #define slic3r_BridgeDetector_hpp_ +#include "ClipperUtils.hpp" +#include "Line.hpp" +#include "Point.hpp" +#include "Polygon.hpp" +#include "Polyline.hpp" +#include "PrincipalComponents2D.hpp" #include "libslic3r.h" #include "ExPolygon.hpp" #include @@ -48,7 +54,7 @@ private: // the best direction is the one causing most lines to be bridged (thus most coverage) bool operator<(const BridgeDirection &other) const { // Initial sort by coverage only - comparator must obey strict weak ordering - return this->archored_percent > other.archored_percent; + return this->coverage > other.coverage;//this->archored_percent > other.archored_percent; }; double angle; double coverage; @@ -65,6 +71,59 @@ private: ExPolygons _anchor_regions; }; + +//return ideal bridge direction and unsupported bridge endpoints distance. +inline std::tuple detect_bridging_direction(const Polygons &to_cover, const Polygons &anchors_area) +{ + Polygons overhang_area = diff(to_cover, anchors_area); + Polylines floating_polylines = diff_pl(to_polylines(overhang_area), expand(anchors_area, float(SCALED_EPSILON))); + + if (floating_polylines.empty()) { + // consider this area anchored from all sides, pick bridging direction that will likely yield shortest bridges + auto [pc1, pc2] = compute_principal_components(overhang_area); + if (pc2 == Vec2f::Zero()) { // overhang may be smaller than resolution. In this case, any direction is ok + return {Vec2d{1.0,0.0}, 0.0}; + } else { + return {pc2.normalized().cast(), 0.0}; + } + } + + // Overhang is not fully surrounded by anchors, in that case, find such direction that will minimize the number of bridge ends/180turns in the air + Lines floating_edges = to_lines(floating_polylines); + std::unordered_map directions{}; + for (const Line &l : floating_edges) { + Vec2d normal = l.normal().cast().normalized(); + double quantized_angle = std::ceil(std::atan2(normal.y(),normal.x()) * 1000.0); + directions.emplace(quantized_angle, normal); + } + std::vector> direction_costs{}; + // it is acutally cost of a perpendicular bridge direction - we find the minimal cost and then return the perpendicular dir + for (const auto& d : directions) { + direction_costs.emplace_back(d.second, 0.0); + } + + for (const Line &l : floating_edges) { + Vec2d line = (l.b - l.a).cast(); + for (auto &dir_cost : direction_costs) { + // the dot product already contains the length of the line. dir_cost.first is normalized. + dir_cost.second += std::abs(line.dot(dir_cost.first)); + } + } + + Vec2d result_dir = Vec2d::Ones(); + double min_cost = std::numeric_limits::max(); + for (const auto &cost : direction_costs) { + if (cost.second < min_cost) { + // now flip the orientation back and return the direction of the bridge extrusions + result_dir = Vec2d{cost.first.y(), -cost.first.x()}; + min_cost = cost.second; + } + } + + return {result_dir, min_cost}; +}; + + } #endif diff --git a/src/libslic3r/Brim.cpp b/src/libslic3r/Brim.cpp index 5989c7ae5f..a9e0b1cc84 100644 --- a/src/libslic3r/Brim.cpp +++ b/src/libslic3r/Brim.cpp @@ -892,10 +892,15 @@ static ExPolygons outer_inner_brim_area(const Print& print, // BBS: inner and outer boundary are offset from the same polygon incase of round off error. auto innerExpoly = offset_ex(ex_poly.contour, brim_offset, jtRound, SCALED_RESOLUTION); append(brim_area_object, diff_ex(offset_ex(innerExpoly, brim_width_mod, jtRound, SCALED_RESOLUTION), innerExpoly)); + } if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btOuterAndInner) { append(brim_area_object, diff_ex(offset_ex(ex_poly_holes_reversed, -brim_offset), offset_ex(ex_poly_holes_reversed, -brim_width - brim_offset))); } + if (brim_type != BrimType::btInnerOnly && brim_type != BrimType::btOuterAndInner) { + // BBS: brim should be apart from holes + append(no_brim_area_object, diff_ex(ex_poly_holes_reversed, offset_ex(ex_poly_holes_reversed, -scale_(5.)))); + } if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btNoBrim) append(no_brim_area_object, diff_ex(offset(ex_poly.contour, no_brim_offset), ex_poly_holes_reversed)); if (brim_type == BrimType::btNoBrim) diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index cbb606adff..b75bfdd385 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -22,6 +22,8 @@ set(lisbslic3r_sources ArcFitter.hpp pchheader.cpp pchheader.hpp + AABBTreeIndirect.hpp + AABBTreeLines.hpp BoundingBox.cpp BoundingBox.hpp BridgeDetector.cpp @@ -51,8 +53,6 @@ set(lisbslic3r_sources enum_bitmask.hpp ExPolygon.cpp ExPolygon.hpp - ExPolygonCollection.cpp - ExPolygonCollection.hpp Extruder.cpp Extruder.hpp ExtrusionEntity.cpp @@ -125,6 +125,8 @@ set(lisbslic3r_sources # GCode/PressureEqualizer.hpp GCode/PrintExtents.cpp GCode/PrintExtents.hpp + GCode/RetractWhenCrossingPerimeters.cpp + GCode/RetractWhenCrossingPerimeters.hpp GCode/SpiralVase.cpp GCode/SpiralVase.hpp GCode/SeamPlacer.cpp @@ -138,6 +140,8 @@ set(lisbslic3r_sources GCode/AvoidCrossingPerimeters.cpp GCode/AvoidCrossingPerimeters.hpp GCode/ExtrusionProcessor.hpp + GCode/ConflictChecker.cpp + GCode/ConflictChecker.hpp GCode.cpp GCode.hpp GCodeReader.cpp @@ -217,6 +221,8 @@ set(lisbslic3r_sources PresetBundle.hpp ProjectTask.cpp ProjectTask.hpp + PrincipalComponents2D.hpp + PrincipalComponents2D.cpp AppConfig.cpp AppConfig.hpp Print.cpp diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index b7aeed8a69..ecd0ab4f5c 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -57,8 +57,113 @@ err: #endif /* CLIPPER_UTILS_DEBUG */ namespace ClipperUtils { - Points EmptyPathsProvider::s_empty_points; - Points SinglePathProvider::s_end; +Points EmptyPathsProvider::s_empty_points; +Points SinglePathProvider::s_end; + +// Clip source polygon to be used as a clipping polygon with a bouding box around the source (to be clipped) polygon. +// Useful as an optimization for expensive ClipperLib operations, for example when clipping source polygons one by one +// with a set of polygons covering the whole layer below. +template inline void clip_clipper_polygon_with_subject_bbox_templ(const std::vector &src, const BoundingBox &bbox, std::vector &out, const bool get_entire_polygons=false) +{ + out.clear(); + const size_t cnt = src.size(); + if (cnt < 3) return; + + enum class Side { Left = 1, Right = 2, Top = 4, Bottom = 8 }; + + auto sides = [bbox](const PointType &p) { + return int(p.x() < bbox.min.x()) * int(Side::Left) + int(p.x() > bbox.max.x()) * int(Side::Right) + int(p.y() < bbox.min.y()) * int(Side::Bottom) + + int(p.y() > bbox.max.y()) * int(Side::Top); + }; + + int sides_prev = sides(src.back()); + int sides_this = sides(src.front()); + const size_t last = cnt - 1; + for (size_t i = 0; i < last; ++i) { + int sides_next = sides(src[i + 1]); + if ( // This point is inside. Take it. + sides_this == 0 || + // Either this point is outside and previous or next is inside, or + // the edge possibly cuts corner of the bounding box. + (sides_prev & sides_this & sides_next) == 0) { + out.emplace_back(src[i]); + sides_prev = sides_this; + } else { + // All the three points (this, prev, next) are outside at the same side. + // Ignore this point. + } + sides_this = sides_next; + } + + // Never produce just a single point output polygon. + if (!out.empty()) + if(get_entire_polygons){ + out=src; + }else{ + if (int sides_next = sides(out.front()); + // The last point is inside. Take it. + sides_this == 0 || + // Either this point is outside and previous or next is inside, or + // the edge possibly cuts corner of the bounding box. + (sides_prev & sides_this & sides_next) == 0) + out.emplace_back(src.back()); + } + +} + +void clip_clipper_polygon_with_subject_bbox(const Points &src, const BoundingBox &bbox, Points &out, const bool get_entire_polygons) { clip_clipper_polygon_with_subject_bbox_templ(src, bbox, out, get_entire_polygons); } +void clip_clipper_polygon_with_subject_bbox(const ZPoints &src, const BoundingBox &bbox, ZPoints &out) { clip_clipper_polygon_with_subject_bbox_templ(src, bbox, out); } + +template [[nodiscard]] std::vector clip_clipper_polygon_with_subject_bbox_templ(const std::vector &src, const BoundingBox &bbox) +{ + std::vector out; + clip_clipper_polygon_with_subject_bbox(src, bbox, out); + return out; +} + +[[nodiscard]] Points clip_clipper_polygon_with_subject_bbox(const Points &src, const BoundingBox &bbox) { return clip_clipper_polygon_with_subject_bbox_templ(src, bbox); } +[[nodiscard]] ZPoints clip_clipper_polygon_with_subject_bbox(const ZPoints &src, const BoundingBox &bbox) { return clip_clipper_polygon_with_subject_bbox_templ(src, bbox); } + +void clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox, Polygon &out) { + clip_clipper_polygon_with_subject_bbox(src.points, bbox, out.points); +} + +[[nodiscard]] Polygon clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox, const bool get_entire_polygons) +{ + Polygon out; + clip_clipper_polygon_with_subject_bbox(src.points, bbox, out.points, get_entire_polygons); + return out; +} + +[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const Polygons &src, const BoundingBox &bbox) +{ + Polygons out; + out.reserve(src.size()); + for (const Polygon &p : src) out.emplace_back(clip_clipper_polygon_with_subject_bbox(p, bbox)); + out.erase(std::remove_if(out.begin(), out.end(), [](const Polygon &polygon) { return polygon.empty(); }), out.end()); + return out; +} +[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygon &src, const BoundingBox &bbox, const bool get_entire_polygons) +{ + Polygons out; + out.reserve(src.num_contours()); + out.emplace_back(clip_clipper_polygon_with_subject_bbox(src.contour, bbox, get_entire_polygons)); + for (const Polygon &p : src.holes) out.emplace_back(clip_clipper_polygon_with_subject_bbox(p, bbox, get_entire_polygons)); + out.erase(std::remove_if(out.begin(), out.end(), [](const Polygon &polygon) { return polygon.empty(); }), out.end()); + return out; +} +[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygons &src, const BoundingBox &bbox, const bool get_entire_polygons) +{ + Polygons out; + out.reserve(number_polygons(src)); + for (const ExPolygon &p : src) { + Polygons temp = clip_clipper_polygons_with_subject_bbox(p, bbox, get_entire_polygons); + out.insert(out.end(), temp.begin(), temp.end()); + } + + out.erase(std::remove_if(out.begin(), out.end(), [](const Polygon &polygon) {return polygon.empty(); }), out.end()); + return out; +} } static ExPolygons PolyTreeToExPolygons(ClipperLib::PolyTree &&polytree) @@ -560,6 +665,8 @@ Slic3r::Polygons diff(const Slic3r::Surfaces &subject, const Slic3r::Polygons &c { return _clipper(ClipperLib::ctDifference, ClipperUtils::SurfacesProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); } Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset) { return _clipper(ClipperLib::ctIntersection, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::SinglePathProvider(clip.points), do_safety_offset); } +Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset) + { return _clipper(ClipperLib::ctIntersection, ClipperUtils::PolygonsProvider(subject), ClipperUtils::ExPolygonProvider(clip), do_safety_offset); } Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) { return _clipper(ClipperLib::ctIntersection, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); } Slic3r::Polygons intersection(const Slic3r::ExPolygon &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset) @@ -646,10 +753,16 @@ Slic3r::ExPolygons intersection_ex(const Slic3r::Polygons &subject, const Slic3r { return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); } Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygon &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) { return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::ExPolygonProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); } +Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygon& subject, const Slic3r::ExPolygon& clip, ApplySafetyOffset do_safety_offset) + { return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::ExPolygonProvider(subject), ClipperUtils::ExPolygonProvider(clip), do_safety_offset); } Slic3r::ExPolygons intersection_ex(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset) { return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::PolygonsProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); } Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) { return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); } +Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygons& subject, const Slic3r::ExPolygon& clip, ApplySafetyOffset do_safety_offset) + { return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::ExPolygonProvider(clip), do_safety_offset);} +Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygon& subject, const Slic3r::ExPolygons& clip, ApplySafetyOffset do_safety_offset) + { return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::ExPolygonProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset);} Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset) { return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); } Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) @@ -745,6 +858,8 @@ Polylines _clipper_pl_closed(ClipperLib::ClipType clipType, PathProvider1 &&subj return retval; } +Slic3r::Polylines diff_pl(const Slic3r::Polyline& subject, const Slic3r::Polygons& clip) + { return _clipper_pl_open(ClipperLib::ctDifference, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::PolygonsProvider(clip)); } Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip) { return _clipper_pl_open(ClipperLib::ctDifference, ClipperUtils::PolylinesProvider(subject), ClipperUtils::PolygonsProvider(clip)); } Slic3r::Polylines diff_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip) @@ -757,6 +872,10 @@ Slic3r::Polylines diff_pl(const Slic3r::Polygons &subject, const Slic3r::Polygon { return _clipper_pl_closed(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip)); } Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip) { return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::PolylinesProvider(subject), ClipperUtils::SinglePathProvider(clip.points)); } +Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip) + { return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::ExPolygonProvider(clip)); } +Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip) + { return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::PolylinesProvider(subject), ClipperUtils::ExPolygonProvider(clip)); } Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::Polygons &clip) { return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::PolygonsProvider(clip)); } Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip) diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index d96061b07b..c36778e827 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -296,7 +296,26 @@ namespace ClipperUtils { const SurfacesPtr &m_surfaces; size_t m_size; }; -} + + + // For ClipperLib with Z coordinates. + using ZPoint = Vec3i32; + using ZPoints = std::vector; + + // Clip source polygon to be used as a clipping polygon with a bouding box around the source (to be clipped) polygon. + // Useful as an optimization for expensive ClipperLib operations, for example when clipping source polygons one by one + // with a set of polygons covering the whole layer below. + void clip_clipper_polygon_with_subject_bbox(const Points &src, const BoundingBox &bbox, Points &out, const bool get_entire_polygons = false); + void clip_clipper_polygon_with_subject_bbox(const ZPoints &src, const BoundingBox &bbox, ZPoints &out); + [[nodiscard]] Points clip_clipper_polygon_with_subject_bbox(const Points &src, const BoundingBox &bbox); + [[nodiscard]] ZPoints clip_clipper_polygon_with_subject_bbox(const ZPoints &src, const BoundingBox &bbox); + void clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox, Polygon &out); + [[nodiscard]] Polygon clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox, const bool get_entire_polygons = false); + [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const Polygons &src, const BoundingBox &bbox); + [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygon &src, const BoundingBox &bbox, const bool get_entire_polygons = false); + [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygons &src, const BoundingBox &bbox, const bool get_entire_polygons = false); + + } // Perform union of input polygons using the non-zero rule, convert to ExPolygons. ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input, bool do_union = false); @@ -412,6 +431,7 @@ Slic3r::ExPolygons diff_ex(const Slic3r::Surfaces &subject, const Slic3r::ExPoly Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons diff_ex(const Slic3r::Surfaces &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons diff_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); +Slic3r::Polylines diff_pl(const Slic3r::Polyline& subject, const Slic3r::Polygons& clip); Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip); Slic3r::Polylines diff_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip); Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip); @@ -452,6 +472,7 @@ inline Slic3r::Lines diff_ln(const Slic3r::Lines &subject, const Slic3r::Polygon // Safety offset is applied to the clipping polygons only. Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); +Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons intersection(const Slic3r::ExPolygon &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons intersection(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); @@ -462,28 +483,24 @@ Slic3r::Polygons intersection(const Slic3r::Surfaces &subject, const Slic3r::E Slic3r::Polygons intersection(const Slic3r::Polygons& subject, const Slic3r::Polygon& clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygon &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); +Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygon& subject, const Slic3r::ExPolygon& clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons intersection_ex(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); +Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygons& subject, const Slic3r::ExPolygon& clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); +Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygon& subject, const Slic3r::ExPolygons& clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons intersection_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip); +Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip); +Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip); Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::Polygons &clip); Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip); Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &clip); Slic3r::Polylines intersection_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip); -// BBS -inline Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygon& subject, const Slic3r::ExPolygons& clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No) -{ - Slic3r::ExPolygons subject_temp; - subject_temp.push_back(subject); - - return intersection_ex(subject_temp, clip, do_safety_offset); -} - inline Slic3r::Lines intersection_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip) { return _clipper_ln(ClipperLib::ctIntersection, subject, clip); diff --git a/src/libslic3r/EdgeGrid.cpp b/src/libslic3r/EdgeGrid.cpp index 1385a51d8d..4985b788e4 100644 --- a/src/libslic3r/EdgeGrid.cpp +++ b/src/libslic3r/EdgeGrid.cpp @@ -136,11 +136,6 @@ void EdgeGrid::Grid::create(const ExPolygons &expolygons, coord_t resolution) create_from_m_contours(resolution); } -void EdgeGrid::Grid::create(const ExPolygonCollection &expolygons, coord_t resolution) -{ - create(expolygons.expolygons, resolution); -} - // m_contours has been initialized. Now fill in the edge grid. void EdgeGrid::Grid::create_from_m_contours(coord_t resolution) { diff --git a/src/libslic3r/EdgeGrid.hpp b/src/libslic3r/EdgeGrid.hpp index 3c99291498..4be2bdd07c 100644 --- a/src/libslic3r/EdgeGrid.hpp +++ b/src/libslic3r/EdgeGrid.hpp @@ -7,7 +7,6 @@ #include "Point.hpp" #include "BoundingBox.hpp" #include "ExPolygon.hpp" -#include "ExPolygonCollection.hpp" namespace Slic3r { namespace EdgeGrid { @@ -112,7 +111,6 @@ public: void create(const std::vector &polygons, coord_t resolution) { this->create(polygons, resolution, false); } void create(const ExPolygon &expoly, coord_t resolution); void create(const ExPolygons &expolygons, coord_t resolution); - void create(const ExPolygonCollection &expolygons, coord_t resolution); const std::vector& contours() const { return m_contours; } @@ -123,7 +121,6 @@ public: bool intersect(const Polygons &polygons) { for (size_t i = 0; i < polygons.size(); ++ i) if (intersect(polygons[i])) return true; return false; } bool intersect(const ExPolygon &expoly) { if (intersect(expoly.contour)) return true; for (size_t i = 0; i < expoly.holes.size(); ++ i) if (intersect(expoly.holes[i])) return true; return false; } bool intersect(const ExPolygons &expolygons) { for (size_t i = 0; i < expolygons.size(); ++ i) if (intersect(expolygons[i])) return true; return false; } - bool intersect(const ExPolygonCollection &expolygons) { return intersect(expolygons.expolygons); } // Test, whether a point is inside a contour. bool inside(const Point &pt); @@ -391,7 +388,7 @@ protected: // Referencing the source contours. // This format allows one to work with any Slic3r fixed point contour format - // (Polygon, ExPolygon, ExPolygonCollection etc). + // (Polygon, ExPolygon, ExPolygons etc). std::vector m_contours; // Referencing a contour and a line segment of m_contours. diff --git a/src/libslic3r/ExPolygon.cpp b/src/libslic3r/ExPolygon.cpp index 98a9362c48..a1b5691ff1 100644 --- a/src/libslic3r/ExPolygon.cpp +++ b/src/libslic3r/ExPolygon.cpp @@ -12,27 +12,6 @@ namespace Slic3r { -ExPolygon::operator Points() const -{ - Points points; - Polygons pp = *this; - for (Polygons::const_iterator poly = pp.begin(); poly != pp.end(); ++poly) { - for (Points::const_iterator point = poly->points.begin(); point != poly->points.end(); ++point) - points.push_back(*point); - } - return points; -} - -ExPolygon::operator Polygons() const -{ - return to_polygons(*this); -} - -ExPolygon::operator Polylines() const -{ - return to_polylines(*this); -} - void ExPolygon::scale(double factor) { contour.scale(factor); @@ -40,6 +19,13 @@ void ExPolygon::scale(double factor) hole.scale(factor); } +void ExPolygon::scale(double factor_x, double factor_y) +{ + contour.scale(factor_x, factor_y); + for (Polygon &hole : holes) + hole.scale(factor_x, factor_y); +} + void ExPolygon::translate(const Point &p) { contour.translate(p); @@ -118,34 +104,53 @@ bool ExPolygon::contains(const Polylines &polylines) const return pl_out.empty(); } -bool ExPolygon::contains(const Point &point) const +bool ExPolygon::contains(const Point &point, bool border_result /* = true */) const { - if (! this->contour.contains(point)) + if (! Slic3r::contains(contour, point, border_result)) + // Outside the outer contour, not on the contour boundary. return false; for (const Polygon &hole : this->holes) - if (hole.contains(point)) + if (Slic3r::contains(hole, point, ! border_result)) + // Inside a hole, not on the hole boundary. return false; return true; } -// inclusive version of contains() that also checks whether point is on boundaries -bool ExPolygon::contains_b(const Point &point) const +bool ExPolygon::on_boundary(const Point &point, double eps) const { - return this->contains(point) || this->has_boundary_point(point); + if (this->contour.on_boundary(point, eps)) + return true; + for (const Polygon &hole : this->holes) + if (hole.on_boundary(point, eps)) + return true; + return false; } -bool -ExPolygon::has_boundary_point(const Point &point) const +// Projection of a point onto the polygon. +Point ExPolygon::point_projection(const Point &point) const { - if (this->contour.has_boundary_point(point)) return true; - for (Polygons::const_iterator h = this->holes.begin(); h != this->holes.end(); ++h) { - if (h->has_boundary_point(point)) return true; + if (this->holes.empty()) { + return this->contour.point_projection(point); + } else { + double dist_min2 = std::numeric_limits::max(); + Point closest_pt_min; + for (size_t i = 0; i < this->num_contours(); ++ i) { + Point closest_pt = this->contour_or_hole(i).point_projection(point); + double d2 = (closest_pt - point).cast().squaredNorm(); + if (d2 < dist_min2) { + dist_min2 = d2; + closest_pt_min = closest_pt; + } + } + return closest_pt_min; } - return false; } bool ExPolygon::overlaps(const ExPolygon &other) const { + if (this->empty() || other.empty()) + return false; + #if 0 BoundingBox bbox = get_extents(other); bbox.merge(get_extents(*this)); @@ -155,61 +160,92 @@ bool ExPolygon::overlaps(const ExPolygon &other) const svg.draw_outline(*this); svg.draw_outline(other, "blue"); #endif - Polylines pl_out = intersection_pl((Polylines)other, *this); + + Polylines pl_out = intersection_pl(to_polylines(other), *this); + #if 0 svg.draw(pl_out, "red"); #endif - if (! pl_out.empty()) - return true; - //FIXME ExPolygon::overlaps() shall be commutative, it is not! - return ! other.contour.points.empty() && this->contains_b(other.contour.points.front()); + + // See unit test SCENARIO("Clipper diff with polyline", "[Clipper]") + // for in which case the intersection_pl produces any intersection. + return ! pl_out.empty() || + // If *this is completely inside other, then pl_out is empty, but the expolygons overlap. Test for that situation. + other.contains(this->contour.points.front()); } -void ExPolygon::simplify_p(double tolerance, Polygons* polygons, SimplifyMethod method) const +bool overlaps(const ExPolygons& expolys1, const ExPolygons& expolys2) { - Polygons pp = this->simplify_p(tolerance, method); + for (const ExPolygon& expoly1 : expolys1) { + for (const ExPolygon& expoly2 : expolys2) { + if (expoly1.overlaps(expoly2)) + return true; + } + } + return false; +} + +Point projection_onto(const ExPolygons& polygons, const Point& from) +{ + Point projected_pt; + double min_dist = std::numeric_limits::max(); + + for (const auto& poly : polygons) { + for (int i = 0; i < poly.num_contours(); i++) { + Point p = from.projection_onto(poly.contour_or_hole(i)); + double dist = (from - p).cast().squaredNorm(); + if (dist < min_dist) { + projected_pt = p; + min_dist = dist; + } + } + } + + return projected_pt; +} + +void ExPolygon::simplify_p(double tolerance, Polygons* polygons) const +{ + Polygons pp = this->simplify_p(tolerance); polygons->insert(polygons->end(), pp.begin(), pp.end()); } -Polygons ExPolygon::simplify_p(double tolerance, SimplifyMethod method) const +Polygons ExPolygon::simplify_p(double tolerance) const { Polygons pp; pp.reserve(this->holes.size() + 1); - std::map> method_list = { {SimplifyMethodDP, MultiPoint::_douglas_peucker}, {SimplifyMethodVisvalingam, MultiPoint::visivalingam},{SimplifyMethodConcave, MultiPoint::concave_hull_2d} }; // contour { Polygon p = this->contour; p.points.push_back(p.points.front()); - p.points = method_list[method](p.points, tolerance); + p.points = MultiPoint::_douglas_peucker(p.points, tolerance); p.points.pop_back(); pp.emplace_back(std::move(p)); } // holes for (Polygon p : this->holes) { p.points.push_back(p.points.front()); - p.points = method_list[method](p.points, tolerance); + p.points = MultiPoint::_douglas_peucker(p.points, tolerance); p.points.pop_back(); pp.emplace_back(std::move(p)); } return simplify_polygons(pp); } -ExPolygons ExPolygon::simplify(double tolerance, SimplifyMethod method) const +ExPolygons ExPolygon::simplify(double tolerance) const { - return union_ex(this->simplify_p(tolerance, method)); + return union_ex(this->simplify_p(tolerance)); } -void ExPolygon::simplify(double tolerance, ExPolygons* expolygons, SimplifyMethod method) const +void ExPolygon::simplify(double tolerance, ExPolygons* expolygons) const { - append(*expolygons, this->simplify(tolerance, method)); + append(*expolygons, this->simplify(tolerance)); } -void -ExPolygon::medial_axis(double max_width, double min_width, ThickPolylines* polylines) const +void ExPolygon::medial_axis(double min_width, double max_width, ThickPolylines* polylines) const { // init helper object - Slic3r::Geometry::MedialAxis ma(max_width, min_width, this); - ma.lines = this->lines(); + Slic3r::Geometry::MedialAxis ma(min_width, max_width, *this); // compute the Voronoi diagram and extract medial axis polylines ThickPolylines pp; @@ -240,7 +276,7 @@ ExPolygon::medial_axis(double max_width, double min_width, ThickPolylines* polyl call, so we keep the inner point until we perform the second intersection() as well */ Point new_front = polyline.points.front(); Point new_back = polyline.points.back(); - if (polyline.endpoints.first && !this->has_boundary_point(new_front)) { + if (polyline.endpoints.first && !this->on_boundary(new_front, SCALED_EPSILON)) { Vec2d p1 = polyline.points.front().cast(); Vec2d p2 = polyline.points[1].cast(); // prevent the line from touching on the other side, otherwise intersection() might return that solution @@ -250,7 +286,7 @@ ExPolygon::medial_axis(double max_width, double min_width, ThickPolylines* polyl p1 -= (p2 - p1).normalized() * max_width; this->contour.intersection(Line(p1.cast(), p2.cast()), &new_front); } - if (polyline.endpoints.second && !this->has_boundary_point(new_back)) { + if (polyline.endpoints.second && !this->on_boundary(new_back, SCALED_EPSILON)) { Vec2d p1 = (polyline.points.end() - 2)->cast(); Vec2d p2 = polyline.points.back().cast(); // prevent the line from touching on the other side, otherwise intersection() might return that solution @@ -312,16 +348,17 @@ ExPolygon::medial_axis(double max_width, double min_width, ThickPolylines* polyl } } } - + polylines->insert(polylines->end(), pp.begin(), pp.end()); } -void -ExPolygon::medial_axis(double max_width, double min_width, Polylines* polylines) const +void ExPolygon::medial_axis(double min_width, double max_width, Polylines* polylines) const { ThickPolylines tp; - this->medial_axis(max_width, min_width, &tp); - polylines->insert(polylines->end(), tp.begin(), tp.end()); + this->medial_axis(min_width, max_width, &tp); + polylines->reserve(polylines->size() + tp.size()); + for (auto &pl : tp) + polylines->emplace_back(pl.points); } Lines ExPolygon::lines() const @@ -334,6 +371,18 @@ Lines ExPolygon::lines() const return lines; } +// Do expolygons match? If they match, they must have the same topology, +// however their contours may be rotated. +bool expolygons_match(const ExPolygon &l, const ExPolygon &r) +{ + if (l.holes.size() != r.holes.size() || ! polygons_match(l.contour, r.contour)) + return false; + for (size_t hole_idx = 0; hole_idx < l.holes.size(); ++ hole_idx) + if (! polygons_match(l.holes[hole_idx], r.holes[hole_idx])) + return false; + return true; +} + BoundingBox get_extents(const ExPolygon &expolygon) { return get_extents(expolygon.contour); diff --git a/src/libslic3r/ExPolygon.hpp b/src/libslic3r/ExPolygon.hpp index 5b03008305..c8f5a5f459 100644 --- a/src/libslic3r/ExPolygon.hpp +++ b/src/libslic3r/ExPolygon.hpp @@ -1,6 +1,7 @@ #ifndef slic3r_ExPolygon_hpp_ #define slic3r_ExPolygon_hpp_ +#include "Point.hpp" #include "libslic3r.h" #include "Polygon.hpp" #include "Polyline.hpp" @@ -9,13 +10,7 @@ namespace Slic3r { class ExPolygon; -typedef std::vector ExPolygons; - -typedef enum SimplifyMethod_ { - SimplifyMethodDP=0, - SimplifyMethodVisvalingam, - SimplifyMethodConcave -}SimplifyMethod; +using ExPolygons = std::vector; class ExPolygon { @@ -37,14 +32,12 @@ public: ExPolygon& operator=(const ExPolygon &other) = default; ExPolygon& operator=(ExPolygon &&other) = default; - Polygon contour; - Polygons holes; + Polygon contour; //CCW + Polygons holes; //CW - operator Points() const; - operator Polygons() const; - operator Polylines() const; void clear() { contour.points.clear(); holes.clear(); } void scale(double factor); + void scale(double factor_x, double factor_y); void translate(double x, double y) { this->translate(Point(coord_t(x), coord_t(y))); } void translate(const Point &vector); void rotate(double angle); @@ -58,21 +51,29 @@ public: bool contains(const Line &line) const; bool contains(const Polyline &polyline) const; bool contains(const Polylines &polylines) const; - bool contains(const Point &point) const; - bool contains_b(const Point &point) const; - bool has_boundary_point(const Point &point) const; + bool contains(const Point &point, bool border_result = true) const; + // Approximate on boundary test. + bool on_boundary(const Point &point, double eps) const; + // Projection of a point onto the polygon. + Point point_projection(const Point &point) const; // Does this expolygon overlap another expolygon? // Either the ExPolygons intersect, or one is fully inside the other, // and it is not inside a hole of the other expolygon. + // The test may not be commutative if the two expolygons touch by a boundary only, + // see unit test SCENARIO("Clipper diff with polyline", "[Clipper]"). + // Namely expolygons touching at a vertical boundary are considered overlapping, while expolygons touching + // at a horizontal boundary are NOT considered overlapping. bool overlaps(const ExPolygon &other) const; - void simplify_p(double tolerance, Polygons* polygons, SimplifyMethod method = SimplifyMethodDP) const; - Polygons simplify_p(double tolerance, SimplifyMethod method = SimplifyMethodDP) const; - ExPolygons simplify(double tolerance, SimplifyMethod method = SimplifyMethodDP) const; - void simplify(double tolerance, ExPolygons* expolygons, SimplifyMethod method = SimplifyMethodDP) const; - void medial_axis(double max_width, double min_width, ThickPolylines* polylines) const; - void medial_axis(double max_width, double min_width, Polylines* polylines) const; + void simplify_p(double tolerance, Polygons* polygons) const; + Polygons simplify_p(double tolerance) const; + ExPolygons simplify(double tolerance) const; + void simplify(double tolerance, ExPolygons* expolygons) const; + void medial_axis(double min_width, double max_width, ThickPolylines* polylines) const; + void medial_axis(double min_width, double max_width, Polylines* polylines) const; + Polylines medial_axis(double min_width, double max_width) const + { Polylines out; this->medial_axis(min_width, max_width, &out); return out; } Lines lines() const; // Number of contours (outer contour with holes). @@ -84,22 +85,22 @@ public: inline bool operator==(const ExPolygon &lhs, const ExPolygon &rhs) { return lhs.contour == rhs.contour && lhs.holes == rhs.holes; } inline bool operator!=(const ExPolygon &lhs, const ExPolygon &rhs) { return lhs.contour != rhs.contour || lhs.holes != rhs.holes; } -inline size_t count_points(const ExPolygons& expolys) +inline size_t count_points(const ExPolygons &expolys) { size_t n_points = 0; - for (const auto& expoly : expolys) { + for (const auto &expoly : expolys) { n_points += expoly.contour.points.size(); - for (const auto& hole : expoly.holes) + for (const auto &hole : expoly.holes) n_points += hole.points.size(); } return n_points; } -inline size_t count_points(const ExPolygon& expoly) +inline size_t count_points(const ExPolygon &expoly) { size_t n_points = expoly.contour.points.size(); - for (const auto& hole : expoly.holes) - n_points += hole.points.size(); + for (const auto &hole : expoly.holes) + n_points += hole.points.size(); return n_points; } @@ -115,11 +116,8 @@ inline size_t number_polygons(const ExPolygons &expolys) inline Lines to_lines(const ExPolygon &src) { - size_t n_lines = src.contour.points.size(); - for (size_t i = 0; i < src.holes.size(); ++ i) - n_lines += src.holes[i].points.size(); Lines lines; - lines.reserve(n_lines); + lines.reserve(count_points(src)); for (size_t i = 0; i <= src.holes.size(); ++ i) { const Polygon &poly = (i == 0) ? src.contour : src.holes[i - 1]; for (Points::const_iterator it = poly.points.begin(); it != poly.points.end()-1; ++it) @@ -131,14 +129,8 @@ inline Lines to_lines(const ExPolygon &src) inline Lines to_lines(const ExPolygons &src) { - size_t n_lines = 0; - for (ExPolygons::const_iterator it_expoly = src.begin(); it_expoly != src.end(); ++ it_expoly) { - n_lines += it_expoly->contour.points.size(); - for (size_t i = 0; i < it_expoly->holes.size(); ++ i) - n_lines += it_expoly->holes[i].points.size(); - } Lines lines; - lines.reserve(n_lines); + lines.reserve(count_points(src)); for (ExPolygons::const_iterator it_expoly = src.begin(); it_expoly != src.end(); ++ it_expoly) { for (size_t i = 0; i <= it_expoly->holes.size(); ++ i) { const Points &points = ((i == 0) ? it_expoly->contour : it_expoly->holes[i - 1]).points; @@ -149,59 +141,6 @@ inline Lines to_lines(const ExPolygons &src) } return lines; } -// Line is from point index(see to_points) to next point. -// Next point of last point in polygon is first polygon point. -inline Linesf to_linesf(const ExPolygons& src, uint32_t count_lines = 0) -{ - assert(count_lines == 0 || count_lines == count_points(src)); - if (count_lines == 0) - count_lines = count_points(src); - Linesf lines; - lines.reserve(count_lines); - Vec2d prev_pd; - auto to_lines = [&lines, &prev_pd](const Points& pts) { - assert(pts.size() >= 3); - if (pts.size() < 2) - return; - bool is_first = true; - for (const Point& p : pts) { - Vec2d pd = p.cast(); - if (is_first) - is_first = false; - else - lines.emplace_back(prev_pd, pd); - prev_pd = pd; - } - lines.emplace_back(prev_pd, pts.front().cast()); - }; - for (const ExPolygon& expoly : src) { - to_lines(expoly.contour.points); - for (const Polygon& hole : expoly.holes) - to_lines(hole.points); - } - assert(lines.size() == count_lines); - return lines; -} - -inline Linesf to_unscaled_linesf(const ExPolygons& src) -{ - Linesf lines; - lines.reserve(count_points(src)); - for (ExPolygons::const_iterator it_expoly = src.begin(); it_expoly != src.end(); ++it_expoly) { - for (size_t i = 0; i <= it_expoly->holes.size(); ++i) { - const Points& points = ((i == 0) ? it_expoly->contour : it_expoly->holes[i - 1]).points; - Vec2d unscaled_a = unscaled(points.front()); - Vec2d unscaled_b = unscaled_a; - for (Points::const_iterator it = points.begin() + 1; it != points.end(); ++it) { - unscaled_b = unscaled(*(it)); - lines.push_back(Linef(unscaled_a, unscaled_b)); - unscaled_a = unscaled_b; - } - lines.push_back(Linef(unscaled_a, unscaled(points.front()))); - } - } - return lines; -} inline Points to_points(const ExPolygons& src) { @@ -216,6 +155,56 @@ inline Points to_points(const ExPolygons& src) return points; } +// Line is from point index(see to_points) to next point. +// Next point of last point in polygon is first polygon point. +inline Linesf to_linesf(const ExPolygons &src, uint32_t count_lines = 0) +{ + assert(count_lines == 0 || count_lines == count_points(src)); + if (count_lines == 0) count_lines = count_points(src); + Linesf lines; + lines.reserve(count_lines); + Vec2d prev_pd; + auto to_lines = [&lines, &prev_pd](const Points &pts) { + assert(pts.size() >= 3); + if (pts.size() < 2) return; + bool is_first = true; + for (const Point &p : pts) { + Vec2d pd = p.cast(); + if (is_first) is_first = false; + else lines.emplace_back(prev_pd, pd); + prev_pd = pd; + } + lines.emplace_back(prev_pd, pts.front().cast()); + }; + for (const ExPolygon& expoly: src) { + to_lines(expoly.contour.points); + for (const Polygon &hole : expoly.holes) + to_lines(hole.points); + } + assert(lines.size() == count_lines); + return lines; +} + +inline Linesf to_unscaled_linesf(const ExPolygons &src) +{ + Linesf lines; + lines.reserve(count_points(src)); + for (ExPolygons::const_iterator it_expoly = src.begin(); it_expoly != src.end(); ++ it_expoly) { + for (size_t i = 0; i <= it_expoly->holes.size(); ++ i) { + const Points &points = ((i == 0) ? it_expoly->contour : it_expoly->holes[i - 1]).points; + Vec2d unscaled_a = unscaled(points.front()); + Vec2d unscaled_b = unscaled_a; + for (Points::const_iterator it = points.begin()+1; it != points.end(); ++it){ + unscaled_b = unscaled(*(it)); + lines.push_back(Linef(unscaled_a, unscaled_b)); + unscaled_a = unscaled_b; + } + lines.push_back(Linef(unscaled_a, unscaled(points.front()))); + } + } + return lines; +} + inline Polylines to_polylines(const ExPolygon &src) { Polylines polylines; @@ -260,10 +249,10 @@ inline Polylines to_polylines(ExPolygon &&src) Polyline &pl = polylines[idx ++]; pl.points = std::move(src.contour.points); pl.points.push_back(pl.points.front()); - for (Polygons::const_iterator ith = src.holes.begin(); ith != src.holes.end(); ++ith) { + for (auto ith = src.holes.begin(); ith != src.holes.end(); ++ith) { Polyline &pl = polylines[idx ++]; pl.points = std::move(ith->points); - pl.points.push_back(ith->points.front()); + pl.points.push_back(pl.points.front()); } assert(idx == polylines.size()); return polylines; @@ -274,14 +263,14 @@ inline Polylines to_polylines(ExPolygons &&src) Polylines polylines; polylines.assign(number_polygons(src), Polyline()); size_t idx = 0; - for (ExPolygons::const_iterator it = src.begin(); it != src.end(); ++it) { + for (auto it = src.begin(); it != src.end(); ++it) { Polyline &pl = polylines[idx ++]; pl.points = std::move(it->contour.points); pl.points.push_back(pl.points.front()); - for (Polygons::const_iterator ith = it->holes.begin(); ith != it->holes.end(); ++ith) { + for (auto ith = it->holes.begin(); ith != it->holes.end(); ++ith) { Polyline &pl = polylines[idx ++]; pl.points = std::move(ith->points); - pl.points.push_back(ith->points.front()); + pl.points.push_back(pl.points.front()); } } assert(idx == polylines.size()); @@ -335,8 +324,9 @@ inline Polygons to_polygons(ExPolygon &&src) Polygons polygons; polygons.reserve(src.holes.size() + 1); polygons.push_back(std::move(src.contour)); - std::move(std::begin(src.holes), std::end(src.holes), std::back_inserter(polygons)); - src.holes.clear(); + polygons.insert(polygons.end(), + std::make_move_iterator(src.holes.begin()), + std::make_move_iterator(src.holes.end())); return polygons; } @@ -344,10 +334,11 @@ inline Polygons to_polygons(ExPolygons &&src) { Polygons polygons; polygons.reserve(number_polygons(src)); - for (ExPolygons::iterator it = src.begin(); it != src.end(); ++it) { - polygons.push_back(std::move(it->contour)); - std::move(std::begin(it->holes), std::end(it->holes), std::back_inserter(polygons)); - it->holes.clear(); + for (ExPolygon& expoly: src) { + polygons.push_back(std::move(expoly.contour)); + polygons.insert(polygons.end(), + std::make_move_iterator(expoly.holes.begin()), + std::make_move_iterator(expoly.holes.end())); } return polygons; } @@ -370,6 +361,16 @@ inline ExPolygons to_expolygons(Polygons &&polys) return ex_polys; } +inline Points to_points(const ExPolygon &expoly) +{ + Points out; + out.reserve(count_points(expoly)); + append(out, expoly.contour.points); + for (const Polygon &hole : expoly.holes) + append(out, hole.points); + return out; +} + inline void polygons_append(Polygons &dst, const ExPolygon &src) { dst.reserve(dst.size() + src.holes.size() + 1); @@ -389,18 +390,20 @@ inline void polygons_append(Polygons &dst, const ExPolygons &src) inline void polygons_append(Polygons &dst, ExPolygon &&src) { dst.reserve(dst.size() + src.holes.size() + 1); - dst.push_back(std::move(src.contour)); - std::move(std::begin(src.holes), std::end(src.holes), std::back_inserter(dst)); - src.holes.clear(); + dst.push_back(std::move(src.contour)); + dst.insert(dst.end(), + std::make_move_iterator(src.holes.begin()), + std::make_move_iterator(src.holes.end())); } inline void polygons_append(Polygons &dst, ExPolygons &&src) { dst.reserve(dst.size() + number_polygons(src)); - for (ExPolygons::iterator it = src.begin(); it != src.end(); ++ it) { - dst.push_back(std::move(it->contour)); - std::move(std::begin(it->holes), std::end(it->holes), std::back_inserter(dst)); - it->holes.clear(); + for (ExPolygon& expoly: src) { + dst.push_back(std::move(expoly.contour)); + dst.insert(dst.end(), + std::make_move_iterator(expoly.holes.begin()), + std::make_move_iterator(expoly.holes.end())); } } @@ -414,21 +417,22 @@ inline void expolygons_append(ExPolygons &dst, ExPolygons &&src) if (dst.empty()) { dst = std::move(src); } else { - std::move(std::begin(src), std::end(src), std::back_inserter(dst)); - src.clear(); + dst.insert(dst.end(), + std::make_move_iterator(src.begin()), + std::make_move_iterator(src.end())); } } inline void expolygons_rotate(ExPolygons &expolys, double angle) { - for (ExPolygons::iterator p = expolys.begin(); p != expolys.end(); ++p) - p->rotate(angle); + for (ExPolygon &expoly : expolys) + expoly.rotate(angle); } -inline bool expolygons_contain(ExPolygons &expolys, const Point &pt) +inline bool expolygons_contain(ExPolygons &expolys, const Point &pt, bool border_result = true) { - for (ExPolygons::iterator p = expolys.begin(); p != expolys.end(); ++p) - if (p->contains(pt)) + for (const ExPolygon &expoly : expolys) + if (expoly.contains(pt, border_result)) return true; return false; } @@ -442,6 +446,14 @@ inline ExPolygons expolygons_simplify(const ExPolygons &expolys, double toleranc return out; } +// Do expolygons match? If they match, they must have the same topology, +// however their contours may be rotated. +bool expolygons_match(const ExPolygon &l, const ExPolygon &r); + +bool overlaps(const ExPolygons& expolys1, const ExPolygons& expolys2); + +Point projection_onto(const ExPolygons& expolys, const Point& pt); + BoundingBox get_extents(const ExPolygon &expolygon); BoundingBox get_extents(const ExPolygons &expolygons); BoundingBox get_extents_rotated(const ExPolygon &poly, double angle); diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp index 207fcc4689..e9c20ee7ef 100644 --- a/src/libslic3r/ExtrusionEntity.cpp +++ b/src/libslic3r/ExtrusionEntity.cpp @@ -1,6 +1,6 @@ #include "ExtrusionEntity.hpp" #include "ExtrusionEntityCollection.hpp" -#include "ExPolygonCollection.hpp" +#include "ExPolygon.hpp" #include "ClipperUtils.hpp" #include "Extruder.hpp" #include "Flow.hpp" @@ -12,14 +12,14 @@ namespace Slic3r { -void ExtrusionPath::intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const +void ExtrusionPath::intersect_expolygons(const ExPolygons &collection, ExtrusionEntityCollection* retval) const { - this->_inflate_collection(intersection_pl(Polylines{ polyline }, collection.expolygons), retval); + this->_inflate_collection(intersection_pl(Polylines{ polyline }, collection), retval); } -void ExtrusionPath::subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const +void ExtrusionPath::subtract_expolygons(const ExPolygons &collection, ExtrusionEntityCollection* retval) const { - this->_inflate_collection(diff_pl(Polylines{ this->polyline }, collection.expolygons), retval); + this->_inflate_collection(diff_pl(Polylines{ this->polyline }, collection), retval); } void ExtrusionPath::clip_end(double distance) diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index 62c822ffac..a60f46983c 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -11,7 +11,8 @@ namespace Slic3r { -class ExPolygonCollection; +class ExPolygon; +using ExPolygons = std::vector; class ExtrusionEntityCollection; class Extruder; @@ -187,12 +188,12 @@ public: size_t size() const { return this->polyline.size(); } bool empty() const { return this->polyline.empty(); } bool is_closed() const { return ! this->empty() && this->polyline.points.front() == this->polyline.points.back(); } - // Produce a list of extrusion paths into retval by clipping this path by ExPolygonCollection. + // Produce a list of extrusion paths into retval by clipping this path by ExPolygons. // Currently not used. - void intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const; - // Produce a list of extrusion paths into retval by removing parts of this path by ExPolygonCollection. + void intersect_expolygons(const ExPolygons &collection, ExtrusionEntityCollection* retval) const; + // Produce a list of extrusion paths into retval by removing parts of this path by ExPolygons. // Currently not used. - void subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const; + void subtract_expolygons(const ExPolygons &collection, ExtrusionEntityCollection* retval) const; void clip_end(double distance); void simplify(double tolerance); double length() const override; diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 18b15d4073..5b0bb503cc 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -490,7 +490,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: params.config = &layerm->region().config(); for (ExPolygon& expoly : surface_fill.expolygons) { - f->no_overlap_expolygons = intersection_ex(surface_fill.no_overlap_expolygons, ExPolygons() = {expoly}); + f->no_overlap_expolygons = intersection_ex(surface_fill.no_overlap_expolygons, ExPolygons() = {expoly}, ApplySafetyOffset::Yes); // Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon. f->spacing = surface_fill.params.spacing; surface_fill.surface.expolygon = std::move(expoly); diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index 6060090641..8cfbd8b12f 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -1636,11 +1636,13 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector arches; - arches.reserve(graph.map_infill_end_point_to_boundary.size()); - for (ContourIntersectionPoint &cp : graph.map_infill_end_point_to_boundary) - if (cp.contour_idx != boundary_idx_unconnected && cp.next_on_contour != &cp && cp.could_connect_next()) - arches.push_back({ &cp, path_length_along_contour_ccw(&cp, cp.next_on_contour, graph.boundary_params[cp.contour_idx].back()) }); - std::sort(arches.begin(), arches.end(), [](const auto &l, const auto &r) { return l.arc_length < r.arc_length; }); + if (!params.dont_sort) { + arches.reserve(graph.map_infill_end_point_to_boundary.size()); + for (ContourIntersectionPoint& cp : graph.map_infill_end_point_to_boundary) + if (cp.contour_idx != boundary_idx_unconnected && cp.next_on_contour != &cp && cp.could_connect_next()) + arches.push_back({ &cp, path_length_along_contour_ccw(&cp, cp.next_on_contour, graph.boundary_params[cp.contour_idx].back()) }); + std::sort(arches.begin(), arches.end(), [](const auto& l, const auto& r) { return l.arc_length < r.arc_length; }); + } //FIXME improve the Traveling Salesman problem with 2-opt and 3-opt local optimization. for (Arc &arc : arches) diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp index d9fd90775a..0c6e77667b 100644 --- a/src/libslic3r/Fill/FillBase.hpp +++ b/src/libslic3r/Fill/FillBase.hpp @@ -77,7 +77,7 @@ struct FillParams //BBS: only used for new top surface pattern float no_extrusion_overlap{ 0.0 }; const PrintRegionConfig* config{ nullptr }; - + bool dont_sort{ false }; // do not sort the lines, just simply connect them }; static_assert(IsTriviallyCopyable::value, "FillParams class is not POD (and it should be - see constructor)."); diff --git a/src/libslic3r/Fill/FillPlanePath.cpp b/src/libslic3r/Fill/FillPlanePath.cpp index 49b02b5428..b867ee83a0 100644 --- a/src/libslic3r/Fill/FillPlanePath.cpp +++ b/src/libslic3r/Fill/FillPlanePath.cpp @@ -6,6 +6,66 @@ namespace Slic3r { +class InfillPolylineClipper : public InfillPolylineOutput { +public: + InfillPolylineClipper(const BoundingBox bbox, const double scale_out) : InfillPolylineOutput(scale_out), m_bbox(bbox) {} + + void add_point(const Vec2d &pt); + Points&& result() { return std::move(m_out); } + bool clips() const override { return true; } + +private: + enum class Side { + Left = 1, + Right = 2, + Top = 4, + Bottom = 8 + }; + + int sides(const Point &p) const { + return int(p.x() < m_bbox.min.x()) * int(Side::Left) + + int(p.x() > m_bbox.max.x()) * int(Side::Right) + + int(p.y() < m_bbox.min.y()) * int(Side::Bottom) + + int(p.y() > m_bbox.max.y()) * int(Side::Top); + }; + + // Bounding box to clip the polyline with. + BoundingBox m_bbox; + + // Classification of the two last points processed. + int m_sides_prev; + int m_sides_this; +}; + +void InfillPolylineClipper::add_point(const Vec2d &fpt) +{ + const Point pt{ this->scaled(fpt) }; + + if (m_out.size() < 2) { + // Collect the two first points and their status. + (m_out.empty() ? m_sides_prev : m_sides_this) = sides(pt); + m_out.emplace_back(pt); + } else { + // Classify the last inserted point, possibly remove it. + int sides_next = sides(pt); + if (// This point is inside. Take it. + m_sides_this == 0 || + // Either this point is outside and previous or next is inside, or + // the edge possibly cuts corner of the bounding box. + (m_sides_prev & m_sides_this & sides_next) == 0) { + // Keep the last point. + m_sides_prev = m_sides_this; + } else { + // All the three points (this, prev, next) are outside at the same side. + // Ignore the last point. + m_out.pop_back(); + } + // And save the current point. + m_out.emplace_back(pt); + m_sides_this = sides_next; + } +} + void FillPlanePath::_fill_surface_single( const FillParams ¶ms, unsigned int thickness_layers, @@ -13,37 +73,52 @@ void FillPlanePath::_fill_surface_single( ExPolygon expolygon, Polylines &polylines_out) { - expolygon.rotate(- direction.first); + expolygon.rotate(-direction.first); - coord_t distance_between_lines = coord_t(scale_(this->spacing) / params.density); - - // align infill across layers using the object's bounding box - // Rotated bounding box of the whole object. - BoundingBox bounding_box = this->bounding_box.rotated(- direction.first); - - Point shift = this->_centered() ? + //FIXME Vojtech: We are not sure whether the user expects the fill patterns on visible surfaces to be aligned across all the islands of a single layer. + // One may align for this->centered() to align the patterns for Archimedean Chords and Octagram Spiral patterns. + const bool align = params.density < 0.995; + + BoundingBox snug_bounding_box = get_extents(expolygon).inflated(SCALED_EPSILON); + + // Rotated bounding box of the area to fill in with the pattern. + BoundingBox bounding_box = align ? + // Sparse infill needs to be aligned across layers. Align infill across layers using the object's bounding box. + this->bounding_box.rotated(-direction.first) : + // Solid infill does not need to be aligned across layers, generate the infill pattern + // around the clipping expolygon only. + snug_bounding_box; + + Point shift = this->centered() ? bounding_box.center() : bounding_box.min; expolygon.translate(-shift.x(), -shift.y()); bounding_box.translate(-shift.x(), -shift.y()); - Pointfs pts = _generate( - coord_t(ceil(coordf_t(bounding_box.min.x()) / distance_between_lines)), - coord_t(ceil(coordf_t(bounding_box.min.y()) / distance_between_lines)), - coord_t(ceil(coordf_t(bounding_box.max.x()) / distance_between_lines)), - coord_t(ceil(coordf_t(bounding_box.max.y()) / distance_between_lines)), - params.resolution); + Polyline polyline; + { + auto distance_between_lines = scaled(this->spacing) / params.density; + auto min_x = coord_t(ceil(coordf_t(bounding_box.min.x()) / distance_between_lines)); + auto min_y = coord_t(ceil(coordf_t(bounding_box.min.y()) / distance_between_lines)); + auto max_x = coord_t(ceil(coordf_t(bounding_box.max.x()) / distance_between_lines)); + auto max_y = coord_t(ceil(coordf_t(bounding_box.max.y()) / distance_between_lines)); + auto resolution = scaled(params.resolution) / distance_between_lines; + if (align) { + // Filling in a bounding box over the whole object, clip generated polyline against the snug bounding box. + snug_bounding_box.translate(-shift.x(), -shift.y()); + InfillPolylineClipper output(snug_bounding_box, distance_between_lines); + this->generate(min_x, min_y, max_x, max_y, resolution, output); + polyline.points = std::move(output.result()); + } else { + // Filling in a snug bounding box, no need to clip. + InfillPolylineOutput output(distance_between_lines); + this->generate(min_x, min_y, max_x, max_y, resolution, output); + polyline.points = std::move(output.result()); + } + } - if (pts.size() >= 2) { - // Convert points to a polyline, upscale. - Polylines polylines(1, Polyline()); - Polyline &polyline = polylines.front(); - polyline.points.reserve(pts.size()); - for (const Vec2d &pt : pts) - polyline.points.emplace_back( - coord_t(floor(pt.x() * distance_between_lines + 0.5)), - coord_t(floor(pt.y() * distance_between_lines + 0.5))); - polylines = intersection_pl(polylines, expolygon); + if (polyline.size() >= 2) { + Polylines polylines = intersection_pl(polyline, expolygon); Polylines chained; if (params.dont_connect() || params.density > 0.5 || polylines.size() <= 1) chained = chain_polylines(std::move(polylines)); @@ -59,7 +134,8 @@ void FillPlanePath::_fill_surface_single( } // Follow an Archimedean spiral, in polar coordinates: r=a+b\theta -Pointfs FillArchimedeanChords::_generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution) +template +static void generate_archimedean_chords(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution, Output &output) { // Radius to achieve. coordf_t rmax = std::sqrt(coordf_t(max_x)*coordf_t(max_x)+coordf_t(max_y)*coordf_t(max_y)) * std::sqrt(2.) + 1.5; @@ -70,15 +146,22 @@ Pointfs FillArchimedeanChords::_generate(coord_t min_x, coord_t min_y, coord_t m coordf_t r = 1; Pointfs out; //FIXME Vojtech: If used as a solid infill, there is a gap left at the center. - out.emplace_back(0, 0); - out.emplace_back(1, 0); + output.add_point({ 0, 0 }); + output.add_point({ 1, 0 }); while (r < rmax) { // Discretization angle to achieve a discretization error lower than resolution. theta += 2. * acos(1. - resolution / r); r = a + b * theta; - out.emplace_back(r * cos(theta), r * sin(theta)); + output.add_point({ r * cos(theta), r * sin(theta) }); } - return out; +} + +void FillArchimedeanChords::generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution, InfillPolylineOutput &output) +{ + if (output.clips()) + generate_archimedean_chords(min_x, min_y, max_x, max_y, resolution, static_cast(output)); + else + generate_archimedean_chords(min_x, min_y, max_x, max_y, resolution, output); } // Adapted from @@ -126,7 +209,8 @@ static inline Point hilbert_n_to_xy(const size_t n) return Point(x, y); } -Pointfs FillHilbertCurve::_generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double /* resolution */) +template +static void generate_hilbert_curve(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, Output &output) { // Minimum power of two square to fit the domain. size_t sz = 2; @@ -140,46 +224,59 @@ Pointfs FillHilbertCurve::_generate(coord_t min_x, coord_t min_y, coord_t max_x, } size_t sz2 = sz * sz; - Pointfs line; - line.reserve(sz2); + output.reserve(sz2); for (size_t i = 0; i < sz2; ++ i) { Point p = hilbert_n_to_xy(i); - line.emplace_back(p.x() + min_x, p.y() + min_y); + output.add_point({ p.x() + min_x, p.y() + min_y }); } - return line; } -Pointfs FillOctagramSpiral::_generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double /* resolution */) +void FillHilbertCurve::generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double /* resolution */, InfillPolylineOutput &output) +{ + if (output.clips()) + generate_hilbert_curve(min_x, min_y, max_x, max_y, static_cast(output)); + else + generate_hilbert_curve(min_x, min_y, max_x, max_y, output); +} + +template +static void generate_octagram_spiral(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, Output &output) { // Radius to achieve. coordf_t rmax = std::sqrt(coordf_t(max_x)*coordf_t(max_x)+coordf_t(max_y)*coordf_t(max_y)) * std::sqrt(2.) + 1.5; // Now unwind the spiral. coordf_t r = 0; coordf_t r_inc = sqrt(2.); - Pointfs out; - out.emplace_back(0., 0.); + output.add_point({ 0., 0. }); while (r < rmax) { r += r_inc; coordf_t rx = r / sqrt(2.); coordf_t r2 = r + rx; - out.emplace_back( r, 0.); - out.emplace_back( r2, rx); - out.emplace_back( rx, rx); - out.emplace_back( rx, r2); - out.emplace_back( 0., r); - out.emplace_back(-rx, r2); - out.emplace_back(-rx, rx); - out.emplace_back(-r2, rx); - out.emplace_back(- r, 0.); - out.emplace_back(-r2, -rx); - out.emplace_back(-rx, -rx); - out.emplace_back(-rx, -r2); - out.emplace_back( 0., -r); - out.emplace_back( rx, -r2); - out.emplace_back( rx, -rx); - out.emplace_back( r2+r_inc, -rx); + output.add_point({ r, 0. }); + output.add_point({ r2, rx }); + output.add_point({ rx, rx }); + output.add_point({ rx, r2 }); + output.add_point({ 0., r }); + output.add_point({-rx, r2 }); + output.add_point({-rx, rx }); + output.add_point({-r2, rx }); + output.add_point({- r, 0. }); + output.add_point({-r2, -rx }); + output.add_point({-rx, -rx }); + output.add_point({-rx, -r2 }); + output.add_point({ 0., -r }); + output.add_point({ rx, -r2 }); + output.add_point({ rx, -rx }); + output.add_point({ r2+r_inc, -rx }); } - return out; +} + +void FillOctagramSpiral::generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double /* resolution */, InfillPolylineOutput &output) +{ + if (output.clips()) + generate_octagram_spiral(min_x, min_y, max_x, max_y, static_cast(output)); + else + generate_octagram_spiral(min_x, min_y, max_x, max_y, output); } } // namespace Slic3r diff --git a/src/libslic3r/Fill/FillPlanePath.hpp b/src/libslic3r/Fill/FillPlanePath.hpp index 075f174336..f05f3209aa 100644 --- a/src/libslic3r/Fill/FillPlanePath.hpp +++ b/src/libslic3r/Fill/FillPlanePath.hpp @@ -13,6 +13,26 @@ namespace Slic3r { // http://user42.tuxfamily.org/math-planepath/ // http://user42.tuxfamily.org/math-planepath/gallery.html +class InfillPolylineOutput { +public: + InfillPolylineOutput(const double scale_out) : m_scale_out(scale_out) {} + + void reserve(size_t n) { m_out.reserve(n); } + void add_point(const Vec2d& pt) { m_out.emplace_back(this->scaled(pt)); } + Points&& result() { return std::move(m_out); } + virtual bool clips() const { return false; } + +protected: + const Point scaled(const Vec2d& fpt) const { return { coord_t(floor(fpt.x() * m_scale_out + 0.5)), coord_t(floor(fpt.y() * m_scale_out + 0.5)) }; } + + // Output polyline. + Points m_out; + +private: + // Scaling coefficient of the generated points before tested against m_bbox and clipped by bbox. + double m_scale_out; +}; + class FillPlanePath : public Fill { public: @@ -27,8 +47,11 @@ protected: Polylines &polylines_out) override; float _layer_angle(size_t idx) const override { return 0.f; } - virtual bool _centered() const = 0; - virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution) = 0; + virtual bool centered() const = 0; + + friend class InfillPolylineClipper; + + virtual void generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution, InfillPolylineOutput &output) = 0; }; class FillArchimedeanChords : public FillPlanePath @@ -38,8 +61,8 @@ public: ~FillArchimedeanChords() override = default; protected: - bool _centered() const override { return true; } - Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution) override; + bool centered() const override { return true; } + void generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution, InfillPolylineOutput &output) override; }; class FillHilbertCurve : public FillPlanePath @@ -49,8 +72,8 @@ public: ~FillHilbertCurve() override = default; protected: - bool _centered() const override { return false; } - Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution) override; + bool centered() const override { return false; } + void generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution, InfillPolylineOutput &output) override; }; class FillOctagramSpiral : public FillPlanePath @@ -60,8 +83,8 @@ public: ~FillOctagramSpiral() override = default; protected: - bool _centered() const override { return true; } - Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution) override; + bool centered() const override { return true; } + void generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution, InfillPolylineOutput &output) override; }; } // namespace Slic3r diff --git a/src/libslic3r/Fill/FillRectilinear.cpp b/src/libslic3r/Fill/FillRectilinear.cpp index 0174863a63..9a28bbd681 100644 --- a/src/libslic3r/Fill/FillRectilinear.cpp +++ b/src/libslic3r/Fill/FillRectilinear.cpp @@ -415,7 +415,7 @@ public: // bool sticks_removed = remove_sticks(polygons_src); // if (sticks_removed) BOOST_LOG_TRIVIAL(error) << "Sticks removed!"; - polygons_outer = aoffset1 == 0 ? polygons_src : offset(polygons_src, float(aoffset1), ClipperLib::jtMiter, miterLimit); + polygons_outer = aoffset1 == 0 ? to_polygons(polygons_src) : offset(polygons_src, float(aoffset1), ClipperLib::jtMiter, miterLimit); if (aoffset2 < 0) polygons_inner = shrink(polygons_outer, float(aoffset1 - aoffset2), ClipperLib::jtMiter, miterLimit); // Filter out contours with zero area or small area, contours with 2 points only. @@ -3165,7 +3165,7 @@ void FillMonotonicLineWGapFill::fill_surface_extrusion(const Surface* surface, c for (ExPolygon& ex : gaps_ex_sorted) { //BBS: Use DP simplify to avoid duplicated points and accelerate medial-axis calculation as well. ex.douglas_peucker(SCALED_RESOLUTION * 0.1); - ex.medial_axis(max, min, &polylines); + ex.medial_axis(min, max, &polylines); } if (!polylines.empty() && !is_bridge(params.extrusion_role)) { diff --git a/src/libslic3r/Format/bbs_3mf.cpp b/src/libslic3r/Format/bbs_3mf.cpp index b622fdfe48..c2e114471d 100644 --- a/src/libslic3r/Format/bbs_3mf.cpp +++ b/src/libslic3r/Format/bbs_3mf.cpp @@ -110,6 +110,7 @@ const std::string BBS_SEAM_PAINTING_VERSION = "BambuStudio:SeamPaintingV const std::string BBS_MM_PAINTING_VERSION = "BambuStudio:MmPaintingVersion"; const std::string BBL_MODEL_ID_TAG = "model_id"; const std::string BBL_MODEL_NAME_TAG = "Title"; +const std::string BBL_ORIGIN_TAG = "Origin"; const std::string BBL_DESIGNER_TAG = "Designer"; const std::string BBL_DESIGNER_USER_ID_TAG = "DesignerUserId"; const std::string BBL_DESIGNER_COVER_FILE_TAG = "DesignerCover"; @@ -121,6 +122,12 @@ const std::string BBL_MODIFICATION_TAG = "ModificationDate"; const std::string BBL_CREATION_DATE_TAG = "CreationDate"; const std::string BBL_APPLICATION_TAG = "Application"; +const std::string BBL_PROFILE_TITLE_TAG = "ProfileTitle"; +const std::string BBL_PROFILE_COVER_TAG = "ProfileCover"; +const std::string BBL_PROFILE_DESCRIPTION_TAG = "ProfileDescription"; +const std::string BBL_PROFILE_USER_ID_TAG = "ProfileUserId"; +const std::string BBL_PROFILE_USER_NAME_TAG = "ProfileUserName"; + const std::string MODEL_FOLDER = "3D/"; const std::string MODEL_EXTENSION = ".model"; const std::string MODEL_FILE = "3D/3dmodel.model"; // << this is the only format of the string which works with CURA @@ -156,7 +163,7 @@ const std::string PROJECT_EMBEDDED_PRINT_PRESETS_FILE = "Metadata/print_setting_ const std::string PROJECT_EMBEDDED_SLICE_PRESETS_FILE = "Metadata/process_settings_"; const std::string PROJECT_EMBEDDED_FILAMENT_PRESETS_FILE = "Metadata/filament_settings_"; const std::string PROJECT_EMBEDDED_PRINTER_PRESETS_FILE = "Metadata/machine_settings_"; - +const std::string CUT_INFORMATION_FILE = "Metadata/cut_information.xml"; const unsigned int AUXILIARY_STR_LEN = 12; const unsigned int METADATA_STR_LEN = 9; @@ -199,6 +206,24 @@ static constexpr const char* ASSEMBLE_ITEM_TAG = "assemble_item"; static constexpr const char* SLICE_HEADER_TAG = "header"; static constexpr const char* SLICE_HEADER_ITEM_TAG = "header_item"; +// text_info +static constexpr const char* TEXT_INFO_TAG = "text_info"; +static constexpr const char* TEXT_ATTR = "text"; +static constexpr const char* FONT_NAME_ATTR = "font_name"; +static constexpr const char* FONT_INDEX_ATTR = "font_index"; +static constexpr const char* FONT_SIZE_ATTR = "font_size"; +static constexpr const char* THICKNESS_ATTR = "thickness"; +static constexpr const char* EMBEDED_DEPTH_ATTR = "embeded_depth"; +static constexpr const char* ROTATE_ANGLE_ATTR = "rotate_angle"; +static constexpr const char* TEXT_GAP_ATTR = "text_gap"; +static constexpr const char* BOLD_ATTR = "bold"; +static constexpr const char* ITALIC_ATTR = "italic"; +static constexpr const char* SURFACE_TEXT_ATTR = "surface_text"; +static constexpr const char* KEEP_HORIZONTAL_ATTR = "keep_horizontal"; +static constexpr const char* HIT_MESH_ATTR = "hit_mesh"; +static constexpr const char* HIT_POSITION_ATTR = "hit_position"; +static constexpr const char* HIT_NORMAL_ATTR = "hit_normal"; + // BBS: encrypt static constexpr const char* RELATIONSHIP_TAG = "Relationship"; static constexpr const char* PID_ATTR = "pid"; @@ -243,11 +268,13 @@ static constexpr const char* BED_TYPE_ATTR = "bed_type"; static constexpr const char* PRINT_SEQUENCE_ATTR = "print_sequence"; static constexpr const char* GCODE_FILE_ATTR = "gcode_file"; static constexpr const char* THUMBNAIL_FILE_ATTR = "thumbnail_file"; +static constexpr const char* TOP_FILE_ATTR = "top_file"; +static constexpr const char* PICK_FILE_ATTR = "pick_file"; static constexpr const char* PATTERN_FILE_ATTR = "pattern_file"; static constexpr const char* PATTERN_BBOX_FILE_ATTR = "pattern_bbox_file"; static constexpr const char* OBJECT_ID_ATTR = "object_id"; static constexpr const char* INSTANCEID_ATTR = "instance_id"; -static constexpr const char* ARRANGE_ORDER_ATTR = "arrange_order"; +static constexpr const char* IDENTIFYID_ATTR = "identify_id"; static constexpr const char* PLATERID_ATTR = "plater_id"; static constexpr const char* PLATER_NAME_ATTR = "plater_name"; static constexpr const char* PLATE_IDX_ATTR = "index"; @@ -363,6 +390,33 @@ bool bbs_get_attribute_value_bool(const char** attributes, unsigned int attribut return (text != nullptr) ? (bool)::atoi(text) : true; } +void add_vec3(std::stringstream &stream, const Slic3r::Vec3f &tr) +{ + for (unsigned r = 0; r < 3; ++r) { + stream << tr(r); + if (r != 2) + stream << " "; + } +} + +Slic3r::Vec3f get_vec3_from_string(const std::string &pos_str) +{ + Slic3r::Vec3f pos(0, 0, 0); + if (pos_str.empty()) + return pos; + + std::vector values; + boost::split(values, pos_str, boost::is_any_of(" "), boost::token_compress_on); + + if (values.size() != 3) + return pos; + + for (int i = 0; i < 3; ++i) + pos(i) = ::atof(values[i].c_str()); + + return pos; +} + Slic3r::Transform3d bbs_get_transform_from_3mf_specs_string(const std::string& mat_str) { // check: https://3mf.io/3d-manufacturing-format/ or https://github.com/3MFConsortium/spec_core/blob/master/3MF%20Core%20Specification.md @@ -608,7 +662,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) { int object_id; int instance_id; - int arrange_order; + int identify_id; }; struct Instance @@ -648,6 +702,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) MetadataList metadata; RepairedMeshErrors mesh_stats; ModelVolumeType part_type; + TextInfo text_info; VolumeMetadata(unsigned int first_triangle_id, unsigned int last_triangle_id, ModelVolumeType type = ModelVolumeType::MODEL_PART) : first_triangle_id(first_triangle_id) @@ -672,6 +727,19 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) VolumeMetadataList volumes; }; + struct CutObjectInfo + { + struct Connector + { + int volume_id; + int type; + float r_tolerance; + float h_tolerance; + }; + CutObjectBase id; + std::vector connectors; + }; + // Map from a 1 based 3MF object ID to a 0 based ModelObject index inside m_model->objects. //typedef std::pair Id; // BBS: encrypt typedef std::map IdToCurrentObjectMap; @@ -680,6 +748,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) //typedef std::map IdToAliasesMap; typedef std::vector InstancesList; typedef std::map IdToMetadataMap; + typedef std::map IdToCutObjectInfoMap; //typedef std::map IdToGeometryMap; typedef std::map> IdToLayerHeightsProfileMap; /*typedef std::map IdToLayerConfigRangesMap; @@ -848,6 +917,11 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) std::string m_designer_cover; ModelInfo model_info; BBLProject project_info; + std::string m_profile_title; + std::string m_profile_cover; + std::string m_Profile_description; + std::string m_profile_user_id; + std::string m_profile_user_name; XML_Parser m_xml_parser; // Error code returned by the application side of the parser. In that case the expat may not reliably deliver the error state @@ -866,6 +940,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) //IdToGeometryMap m_orig_geometries; // backup & restore CurrentConfig m_curr_config; IdToMetadataMap m_objects_metadata; + IdToCutObjectInfoMap m_cut_object_infos; IdToLayerHeightsProfileMap m_layer_heights_profiles; /*IdToLayerConfigRangesMap m_layer_config_ranges; IdToSlaSupportPointsMap m_sla_support_points; @@ -924,6 +999,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) bool _extract_xml_from_archive(mz_zip_archive& archive, std::string const & path, XML_StartElementHandler start_handler, XML_EndElementHandler end_handler); bool _extract_xml_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, XML_StartElementHandler start_handler, XML_EndElementHandler end_handler); bool _extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); + void _extract_cut_information_from_archive(mz_zip_archive &archive, const mz_zip_archive_file_stat &stat, ConfigSubstitutionContext &config_substitutions); void _extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); void _extract_layer_config_ranges_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions); void _extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); @@ -1031,6 +1107,9 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) bool _handle_start_assemble_item(const char** attributes, unsigned int num_attributes); bool _handle_end_assemble_item(); + bool _handle_start_text_info_item(const char **attributes, unsigned int num_attributes); + bool _handle_end_text_info_item(); + // BBS: callbacks to parse the .rels file static void XMLCALL _handle_start_relationships_element(void* userData, const char* name, const char** attributes); static void XMLCALL _handle_end_relationships_element(void* userData, const char* name); @@ -1128,7 +1207,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) m_plater_data.clear(); m_curr_instance.object_id = -1; m_curr_instance.instance_id = -1; - m_curr_instance.arrange_order = 0; + m_curr_instance.identify_id = 0; clear_errors(); // restore @@ -1380,6 +1459,14 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) m_model->design_info->Designer = m_designer; } + m_model->profile_info = std::make_shared(); + m_model->profile_info->ProfileTile = m_profile_title; + m_model->profile_info->ProfileCover = m_profile_cover; + m_model->profile_info->ProfileDescription = m_Profile_description; + m_model->profile_info->ProfileUserId = m_profile_user_id; + m_model->profile_info->ProfileUserName = m_profile_user_name; + + m_model->model_info = std::make_shared(); m_model->model_info->load(model_info); @@ -1445,6 +1532,10 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) // extract slic3r print config file _extract_project_config_from_archive(archive, stat, config, config_substitutions, model); } + else if (boost::algorithm::iequals(name, CUT_INFORMATION_FILE)) { + // extract object cut info + _extract_cut_information_from_archive(archive, stat, config_substitutions); + } //BBS: project embedded presets else if (!dont_load_config && boost::algorithm::istarts_with(name, PROJECT_EMBEDDED_PRINT_PRESETS_FILE)) { // extract slic3r layer config ranges file @@ -1668,6 +1759,19 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) if (!_generate_volumes_new(*model_object, object_id_list, *volumes_ptr, config_substitutions)) return false; + + // Apply cut information for object if any was loaded + // m_cut_object_ids are indexed by a 1 based model object index. + IdToCutObjectInfoMap::iterator cut_object_info = m_cut_object_infos.find(object.second + 1); + if (cut_object_info != m_cut_object_infos.end()) { + model_object->cut_id = cut_object_info->second.id; + + for (auto connector : cut_object_info->second.connectors) { + assert(0 <= connector.volume_id && connector.volume_id <= int(model_object->volumes.size())); + model_object->volumes[connector.volume_id]->cut_info = + ModelVolume::CutInfo(CutConnectorType(connector.type), connector.r_tolerance, connector.h_tolerance, true); + } + } } // If instances contain a single volume, the volume offset should be 0,0,0 @@ -1776,12 +1880,15 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) plate_data_list[it->first-1]->slice_filaments_info = it->second->slice_filaments_info; plate_data_list[it->first-1]->warnings = it->second->warnings; plate_data_list[it->first-1]->thumbnail_file = (m_load_restore || it->second->thumbnail_file.empty()) ? it->second->thumbnail_file : m_backup_path + "/" + it->second->thumbnail_file; - plate_data_list[it->first-1]->pattern_file = (m_load_restore || it->second->pattern_file.empty()) ? it->second->pattern_file : m_backup_path + "/" + it->second->pattern_file; + //plate_data_list[it->first-1]->pattern_file = (m_load_restore || it->second->pattern_file.empty()) ? it->second->pattern_file : m_backup_path + "/" + it->second->pattern_file; + plate_data_list[it->first-1]->top_file = (m_load_restore || it->second->top_file.empty()) ? it->second->top_file : m_backup_path + "/" + it->second->top_file; + plate_data_list[it->first-1]->pick_file = (m_load_restore || it->second->pick_file.empty()) ? it->second->pick_file : m_backup_path + "/" + it->second->pick_file; plate_data_list[it->first-1]->pattern_bbox_file = (m_load_restore || it->second->pattern_bbox_file.empty()) ? it->second->pattern_bbox_file : m_backup_path + "/" + it->second->pattern_bbox_file; plate_data_list[it->first-1]->config = it->second->config; current_plate_data = plate_data_list[it->first - 1]; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", plate %1%, thumbnail_file=%2%")%it->first %plate_data_list[it->first-1]->thumbnail_file; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", top_thumbnail_file=%1%, pick_thumbnail_file=%2%")%plate_data_list[it->first-1]->top_file %plate_data_list[it->first-1]->pick_file; it++; //update the arrange order @@ -1817,7 +1924,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) continue; } ModelInstance* inst = obj->instances[inst_index]; - inst->arrange_order = map_it->second.second; + inst->loaded_id = map_it->second.second; map_it++; } } @@ -2022,6 +2129,61 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) return true; } + void _BBS_3MF_Importer::_extract_cut_information_from_archive(mz_zip_archive &archive, const mz_zip_archive_file_stat &stat, ConfigSubstitutionContext &config_substitutions) + { + if (stat.m_uncomp_size > 0) { + std::string buffer((size_t) stat.m_uncomp_size, 0); + mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void *) buffer.data(), (size_t) stat.m_uncomp_size, 0); + if (res == 0) { + add_error("Error while reading cut information data to buffer"); + return; + } + + std::istringstream iss(buffer); // wrap returned xml to istringstream + pt::ptree objects_tree; + pt::read_xml(iss, objects_tree); + + for (const auto &object : objects_tree.get_child("objects")) { + pt::ptree object_tree = object.second; + int obj_idx = object_tree.get(".id", -1); + if (obj_idx <= 0) { + add_error("Found invalid object id"); + continue; + } + + IdToCutObjectInfoMap::iterator object_item = m_cut_object_infos.find(obj_idx); + if (object_item != m_cut_object_infos.end()) { + add_error("Found duplicated cut_object_id"); + continue; + } + + CutObjectBase cut_id; + std::vector connectors; + + for (const auto &obj_cut_info : object_tree) { + if (obj_cut_info.first == "cut_id") { + pt::ptree cut_id_tree = obj_cut_info.second; + cut_id = CutObjectBase(ObjectID(cut_id_tree.get(".id")), cut_id_tree.get(".check_sum"), + cut_id_tree.get(".connectors_cnt")); + } + if (obj_cut_info.first == "connectors") { + pt::ptree cut_connectors_tree = obj_cut_info.second; + for (const auto &cut_connector : cut_connectors_tree) { + if (cut_connector.first != "connector") continue; + pt::ptree connector_tree = cut_connector.second; + CutObjectInfo::Connector connector = {connector_tree.get(".volume_id"), connector_tree.get(".type"), + connector_tree.get(".r_tolerance"), connector_tree.get(".h_tolerance")}; + connectors.emplace_back(connector); + } + } + } + + CutObjectInfo cut_info{cut_id, connectors}; + m_cut_object_infos.insert({obj_idx, cut_info}); + } + } + } + void _BBS_3MF_Importer::_extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, const std::string& archive_filename) { if (stat.m_uncomp_size > 0) { @@ -2709,6 +2871,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) res = _handle_start_assemble(attributes, num_attributes); else if (::strcmp(ASSEMBLE_ITEM_TAG, name) == 0) res = _handle_start_assemble_item(attributes, num_attributes); + else if (::strcmp(TEXT_INFO_TAG, name) == 0) + res = _handle_start_text_info_item(attributes, num_attributes); if (!res) _stop_xml_parser(); @@ -3075,7 +3239,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) bool _BBS_3MF_Importer::_handle_start_component(const char** attributes, unsigned int num_attributes) { - int object_id = bbs_get_attribute_value_int(attributes, num_attributes, OBJECTID_ATTR); + std::string path = bbs_get_attribute_value_string(attributes, num_attributes, PPATH_ATTR); + int object_id = bbs_get_attribute_value_int(attributes, num_attributes, OBJECTID_ATTR); Transform3d transform = bbs_get_transform_from_3mf_specs_string(bbs_get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR)); /*Id id = std::make_pair(m_sub_model_path, object_id); @@ -3089,7 +3254,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) }*/ if (m_curr_object) { - Id id = std::make_pair(m_sub_model_path, object_id); + Id id = std::make_pair(m_sub_model_path.empty() ? path : m_sub_model_path, object_id); m_curr_object->components.emplace_back(id, transform); } @@ -3158,7 +3323,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) bool _BBS_3MF_Importer::_handle_end_metadata() { if ((m_curr_metadata_name == BBS_3MF_VERSION)||(m_curr_metadata_name == BBS_3MF_VERSION1)) { - m_is_bbl_3mf = true; + //m_is_bbl_3mf = true; m_version = (unsigned int)atoi(m_curr_characters.c_str()); /*if (m_check_version && (m_version > VERSION_BBS_3MF_COMPATIBLE)) { // std::string msg = _(L("The selected 3mf file has been saved with a newer version of " + std::string(SLIC3R_APP_NAME) + " and is not compatible.")); @@ -3195,6 +3360,9 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) } else if (m_curr_metadata_name == BBL_MODEL_NAME_TAG) { BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found model name = " << m_curr_characters; model_info.model_name = xml_unescape(m_curr_characters); + } else if (m_curr_metadata_name == BBL_ORIGIN_TAG) { + BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found model name = " << m_curr_characters; + model_info.origin = xml_unescape(m_curr_characters); } else if (m_curr_metadata_name == BBL_DESIGNER_TAG) { BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found designer = " << m_curr_characters; m_designer = xml_unescape(m_curr_characters); @@ -3216,6 +3384,21 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) } else if (m_curr_metadata_name == BBL_REGION_TAG) { BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found region = " << m_curr_characters; m_contry_code = xml_unescape(m_curr_characters); + } else if (m_curr_metadata_name == BBL_PROFILE_TITLE_TAG) { + BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found profile_title = " << m_curr_characters; + m_profile_title = xml_unescape(m_curr_characters); + } else if (m_curr_metadata_name == BBL_PROFILE_COVER_TAG) { + BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found profile_cover = " << m_curr_characters; + m_profile_cover = xml_unescape(m_curr_characters); + } else if (m_curr_metadata_name == BBL_PROFILE_DESCRIPTION_TAG) { + BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found profile_description = " << m_curr_characters; + m_Profile_description = xml_unescape(m_curr_characters); + } else if (m_curr_metadata_name == BBL_PROFILE_USER_ID_TAG) { + BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found profile_user_id = " << m_curr_characters; + m_profile_user_id = xml_unescape(m_curr_characters); + }else if (m_curr_metadata_name == BBL_PROFILE_USER_NAME_TAG) { + BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found profile_user_name = " << m_curr_characters; + m_profile_user_name = xml_unescape(m_curr_characters); } else if (m_curr_metadata_name == BBL_CREATION_DATE_TAG) { ; } else if (m_curr_metadata_name == BBL_MODIFICATION_TAG) { @@ -3497,10 +3680,18 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) { m_curr_plater->thumbnail_file = value; } - else if (key == PATTERN_FILE_ATTR) + else if (key == TOP_FILE_ATTR) { - m_curr_plater->pattern_file = value; + m_curr_plater->top_file = value; } + else if (key == PICK_FILE_ATTR) + { + m_curr_plater->pick_file = value; + } + //else if (key == PATTERN_FILE_ATTR) + //{ + // m_curr_plater->pattern_file = value; + //} else if (key == PATTERN_BBOX_FILE_ATTR) { m_curr_plater->pattern_bbox_file = value; @@ -3509,9 +3700,9 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) { m_curr_instance.instance_id = atoi(value.c_str()); } - else if (key == ARRANGE_ORDER_ATTR) + else if (key == IDENTIFYID_ATTR) { - m_curr_instance.arrange_order = atoi(value.c_str()); + m_curr_instance.identify_id = atoi(value.c_str()); } else if (key == OBJECT_ID_ATTR) { @@ -3666,13 +3857,13 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) //add_error("invalid object id/instance id"); //skip this instance m_curr_instance.object_id = m_curr_instance.instance_id = -1; - m_curr_instance.arrange_order = 0; + m_curr_instance.identify_id = 0; return true; } - m_curr_plater->obj_inst_map.emplace(m_curr_instance.object_id, std::make_pair(m_curr_instance.instance_id, m_curr_instance.arrange_order)); + m_curr_plater->obj_inst_map.emplace(m_curr_instance.object_id, std::make_pair(m_curr_instance.instance_id, m_curr_instance.identify_id)); m_curr_instance.object_id = m_curr_instance.instance_id = -1; - m_curr_instance.arrange_order = 0; + m_curr_instance.identify_id = 0; return true; } @@ -3721,6 +3912,56 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) return true; } + bool _BBS_3MF_Importer::_handle_start_text_info_item(const char **attributes, unsigned int num_attributes) + { + IdToMetadataMap::iterator object = m_objects_metadata.find(m_curr_config.object_id); + if (object == m_objects_metadata.end()) { + add_error("can not find object for text_info, id " + std::to_string(m_curr_config.object_id)); + return false; + } + if ((m_curr_config.volume_id == -1) || ((object->second.volumes.size() - 1) < m_curr_config.volume_id)) { + add_error("can not find part for text_info"); + return false; + } + + ObjectMetadata::VolumeMetadata &volume = object->second.volumes[m_curr_config.volume_id]; + + TextInfo text_info; + text_info.m_text = bbs_get_attribute_value_string(attributes, num_attributes, TEXT_ATTR); + text_info.m_font_name = bbs_get_attribute_value_string(attributes, num_attributes, FONT_NAME_ATTR); + + text_info.m_curr_font_idx = bbs_get_attribute_value_int(attributes, num_attributes, FONT_INDEX_ATTR); + + text_info.m_font_size = bbs_get_attribute_value_float(attributes, num_attributes, FONT_SIZE_ATTR); + text_info.m_thickness = bbs_get_attribute_value_float(attributes, num_attributes, THICKNESS_ATTR); + text_info.m_embeded_depth = bbs_get_attribute_value_float(attributes, num_attributes, EMBEDED_DEPTH_ATTR); + text_info.m_rotate_angle = bbs_get_attribute_value_float(attributes, num_attributes, ROTATE_ANGLE_ATTR); + text_info.m_text_gap = bbs_get_attribute_value_float(attributes, num_attributes, TEXT_GAP_ATTR); + + text_info.m_bold = bbs_get_attribute_value_int(attributes, num_attributes, BOLD_ATTR); + text_info.m_italic = bbs_get_attribute_value_int(attributes, num_attributes, ITALIC_ATTR); + text_info.m_is_surface_text = bbs_get_attribute_value_int(attributes, num_attributes, SURFACE_TEXT_ATTR); + text_info.m_keep_horizontal = bbs_get_attribute_value_int(attributes, num_attributes, KEEP_HORIZONTAL_ATTR); + + text_info.m_rr.mesh_id = bbs_get_attribute_value_int(attributes, num_attributes, HIT_MESH_ATTR); + + std::string hit_pos = bbs_get_attribute_value_string(attributes, num_attributes, HIT_POSITION_ATTR); + if (!hit_pos.empty()) + text_info.m_rr.hit = get_vec3_from_string(hit_pos); + + std::string hit_normal = bbs_get_attribute_value_string(attributes, num_attributes, HIT_NORMAL_ATTR); + if (!hit_normal.empty()) + text_info.m_rr.normal = get_vec3_from_string(hit_normal); + + volume.text_info = text_info; + return true; + } + + bool _BBS_3MF_Importer::_handle_end_text_info_item() + { + return true; + } + void XMLCALL _BBS_3MF_Importer::_handle_start_relationships_element(void* userData, const char* name, const char** attributes) { _BBS_3MF_Importer* importer = (_BBS_3MF_Importer*)userData; @@ -3906,16 +4147,17 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) TriangleMesh triangle_mesh(std::move(its), volume_data->mesh_stats); - if (!m_is_bbl_3mf) { - // if the 3mf was not produced by OrcaSlicer and there is only one instance, - // bake the transformation into the geometry to allow the reload from disk command - // to work properly - if (object.instances.size() == 1) { - triangle_mesh.transform(object.instances.front()->get_transformation().get_matrix(), false); - object.instances.front()->set_transformation(Slic3r::Geometry::Transformation()); - //FIXME do the mesh fixing? - } - } + // BBS: no need to multiply the instance matrix into the volume + //if (!m_is_bbl_3mf) { + // // if the 3mf was not produced by BambuStudio and there is only one instance, + // // bake the transformation into the geometry to allow the reload from disk command + // // to work properly + // if (object.instances.size() == 1) { + // triangle_mesh.transform(object.instances.front()->get_transformation().get_matrix(), false); + // object.instances.front()->set_transformation(Slic3r::Geometry::Transformation()); + // //FIXME do the mesh fixing? + // } + //} if (triangle_mesh.volume() < 0) triangle_mesh.flip_triangles(); @@ -3972,6 +4214,9 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) volume->set_type(volume_data->part_type); + if (!volume_data->text_info.m_text.empty()) + volume->set_text_info(volume_data->text_info); + // apply the remaining volume's metadata for (const Metadata& metadata : volume_data->metadata) { if (metadata.key == NAME_KEY) @@ -4716,6 +4961,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) bool m_save_gcode { false }; // whether to save gcode for normal save bool m_skip_model { false }; // skip model when exporting .gcode.3mf bool m_skip_auxiliary { false }; // skip normal axuiliary files + bool m_use_loaded_id { false }; // whether to use loaded id for identify_id public: //BBS: add plate data related logic @@ -4734,6 +4980,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) std::vector& project_presets, const DynamicPrintConfig* config, const std::vector& thumbnail_data, + const std::vector& top_thumbnail_data, + const std::vector& pick_thumbnail_data, Export3mfProgressFn proFn, const std::vector& calibration_data, const std::vector& id_bboxes, @@ -4744,7 +4992,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) bool _add_content_types_file_to_archive(mz_zip_archive& archive); - bool _add_thumbnail_file_to_archive(mz_zip_archive& archive, const ThumbnailData& thumbnail_data, int index); + bool _add_thumbnail_file_to_archive(mz_zip_archive& archive, const ThumbnailData& thumbnail_data, const char* local_path, int index); bool _add_calibration_file_to_archive(mz_zip_archive& archive, const ThumbnailData& thumbnail_data, int index); bool _add_bbox_file_to_archive(mz_zip_archive& archive, const PlateBBoxData& id_bboxes, int index); bool _add_relationships_file_to_archive(mz_zip_archive & archive, @@ -4767,7 +5015,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) bool _add_project_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config, Model& model); //BBS: add project embedded preset files bool _add_project_embedded_presets_to_archive(mz_zip_archive& archive, Model& model, std::vector project_presets); - bool _add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list, const IdToObjectDataMap &objects_data, int export_plate_idx = -1, bool save_gcode = true); + bool _add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list, const IdToObjectDataMap &objects_data, int export_plate_idx = -1, bool save_gcode = true, bool use_loaded_id = false); + bool _add_cut_information_file_to_archive(mz_zip_archive &archive, Model &model); bool _add_slice_info_config_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list); bool _add_gcode_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list, Export3mfProgressFn proFn = nullptr); bool _add_custom_gcode_per_print_z_file_to_archive(mz_zip_archive& archive, Model& model, const DynamicPrintConfig* config); @@ -4801,12 +5050,15 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) m_skip_model = store_params.strategy & SaveStrategy::SkipModel; m_skip_auxiliary = store_params.strategy & SaveStrategy::SkipAuxiliary; + m_use_loaded_id = store_params.strategy & SaveStrategy::UseLoadedId; + boost::system::error_code ec; std::string filename = std::string(store_params.path); boost::filesystem::remove(filename + ".tmp", ec); bool result = _save_model_to_file(filename + ".tmp", *store_params.model, store_params.plate_data_list, store_params.project_presets, store_params.config, - store_params.thumbnail_data, store_params.proFn, store_params.calibration_thumbnail_data, store_params.id_bboxes, store_params.project, store_params.export_plate_idx); + store_params.thumbnail_data, store_params.top_thumbnail_data, store_params.pick_thumbnail_data, store_params.proFn, + store_params.calibration_thumbnail_data, store_params.id_bboxes, store_params.project, store_params.export_plate_idx); if (result) { boost::filesystem::rename(filename + ".tmp", filename, ec); if (ec) { @@ -4874,6 +5126,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) std::vector& project_presets, const DynamicPrintConfig* config, const std::vector& thumbnail_data, + const std::vector& top_thumbnail_data, + const std::vector& pick_thumbnail_data, Export3mfProgressFn proFn, const std::vector& calibration_data, const std::vector& id_bboxes, @@ -4888,7 +5142,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) bool cb_cancel = false; //BBS progress point - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(",before open zip writer, m_skip_static %1%, m_save_gcode %2%\n")%m_skip_static %m_save_gcode; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << + boost::format(",before open zip writer, m_skip_static %1%, m_save_gcode %2%, m_use_loaded_id %3%")%m_skip_static %m_save_gcode %m_use_loaded_id; if (proFn) { proFn(EXPORT_STAGE_OPEN_3MF, 0, 1, cb_cancel); if (cb_cancel) @@ -4927,43 +5182,115 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) } //BBS progress point - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(",before add thumbnails, count %1%\n") % thumbnail_data.size(); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(",before add thumbnails, count %1%") % thumbnail_data.size(); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(",top&&pick thumbnails, count %1%")%top_thumbnail_data.size(); //BBS: add thumbnail for each plate - if (!m_skip_static && thumbnail_data.size() > 0) { - // Adds the file Metadata/thumbnail.png. + if (!m_skip_static) { + std::vector thumbnail_status(plate_data_list.size(), false); + std::vector top_thumbnail_status(plate_data_list.size(), false); + std::vector pick_thumbnail_status(plate_data_list.size(), false); + + if ((thumbnail_data.size() > 0)&&(thumbnail_data.size() > plate_data_list.size())) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", thumbnail_data size %1% > plate count %2%") + % thumbnail_data.size() %plate_data_list.size(); + return false; + } + if ((top_thumbnail_data.size() > 0)&&(top_thumbnail_data.size() > plate_data_list.size())) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", top_thumbnail_data size %1% > plate count %2%") + % top_thumbnail_data.size() %plate_data_list.size(); + return false; + } + if ((pick_thumbnail_data.size() > 0)&&(pick_thumbnail_data.size() > plate_data_list.size())) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", pick_thumbnail_data size %1% > plate count %2%") + % pick_thumbnail_data.size() %plate_data_list.size(); + return false; + } + if (top_thumbnail_data.size() != pick_thumbnail_data.size()) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", top_thumbnail_data size %1% != pick_thumbnail_data size %2%") + % top_thumbnail_data.size() %pick_thumbnail_data.size(); + return false; + } + + if (proFn) { + proFn(EXPORT_STAGE_ADD_THUMBNAILS, 0, plate_data_list.size(), cb_cancel); + if (cb_cancel) + return false; + } + for (unsigned int index = 0; index < thumbnail_data.size(); index++) { - if (proFn) { - proFn(EXPORT_STAGE_ADD_THUMBNAILS, index, thumbnail_data.size(), cb_cancel); - if (cb_cancel) - return false; - } - if (thumbnail_data[index]->is_valid()) { - if (!_add_thumbnail_file_to_archive(archive, *thumbnail_data[index], index)) { + if (!_add_thumbnail_file_to_archive(archive, *thumbnail_data[index], "Metadata/plate", index)) { return false; } + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(",add thumbnail %1%'s data into 3mf")%(index+1); + thumbnail_status[index] = true; } } - } - else if (!m_skip_static && plate_data_list.size() > 0) { + + // Adds the file Metadata/top_i.png and Metadata/pick_i.png + for (unsigned int index = 0; index < top_thumbnail_data.size(); index++) + { + if (top_thumbnail_data[index]->is_valid()) + { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(",add top thumbnail %1%'s data into 3mf")%(index+1); + if (!_add_thumbnail_file_to_archive(archive, *top_thumbnail_data[index], "Metadata/top", index)) { + return false; + } + top_thumbnail_status[index] = true; + } + + if (pick_thumbnail_data[index]->is_valid()) + { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(",add pick thumbnail %1%'s data into 3mf")%(index+1); + if (!_add_thumbnail_file_to_archive(archive, *pick_thumbnail_data[index], "Metadata/pick", index)) { + return false; + } + pick_thumbnail_status[index] = true; + } + } + for (int i = 0; i < plate_data_list.size(); i++) { PlateData *plate_data = plate_data_list[i]; - if (proFn) { - proFn(EXPORT_STAGE_ADD_THUMBNAILS, i, plate_data_list.size(), cb_cancel); - if (cb_cancel) - return false; - } - if (!plate_data->thumbnail_file.empty() && (boost::filesystem::exists(plate_data->thumbnail_file))){ + + if (!thumbnail_status[i] && !plate_data->thumbnail_file.empty() && (boost::filesystem::exists(plate_data->thumbnail_file))){ std::string dst_in_3mf = (boost::format("Metadata/plate_%1%.png") % (i + 1)).str(); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", add thumbnail %1% from file %2%") % (i+1) %plate_data->thumbnail_file; if (!_add_file_to_archive(archive, dst_in_3mf, plate_data->thumbnail_file)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", add thumbnail %1% from file %2% failed\n") % (i+1) %plate_data->thumbnail_file; return false; } } + + if (!top_thumbnail_status[i] && !plate_data->top_file.empty() && (boost::filesystem::exists(plate_data->top_file))){ + std::string dst_in_3mf = (boost::format("Metadata/top_%1%.png") % (i + 1)).str(); + + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", add top thumbnail %1% from file %2%") % (i+1) %plate_data->top_file; + if (!_add_file_to_archive(archive, dst_in_3mf, plate_data->top_file)) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", add top thumbnail %1% failed") % (i+1); + return false; + } + top_thumbnail_status[i] = true; + } + + if (!pick_thumbnail_status[i] && !plate_data->pick_file.empty() && (boost::filesystem::exists(plate_data->pick_file))){ + std::string dst_in_3mf = (boost::format("Metadata/pick_%1%.png") % (i + 1)).str(); + + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", add pick thumbnail %1% from file %2%") % (i+1) %plate_data->pick_file; + if (!_add_file_to_archive(archive, dst_in_3mf, plate_data->pick_file)) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", add pick thumbnail %1% failed") % (i+1); + return false; + } + pick_thumbnail_status[i] = true; + } + } + if (proFn) { + proFn(EXPORT_STAGE_ADD_THUMBNAILS, plate_data_list.size(), plate_data_list.size(), cb_cancel); + if (cb_cancel) + return false; } } @@ -5155,11 +5482,16 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) // This file contains all the attributes of all ModelObjects and their ModelVolumes (names, parameter overrides). // As there is just a single Indexed Triangle Set data stored per ModelObject, offsets of volumes into their respective Indexed Triangle Set data // is stored here as well. - if (!_add_model_config_file_to_archive(archive, model, plate_data_list, objects_data, export_plate_idx, m_save_gcode)) { + if (!_add_model_config_file_to_archive(archive, model, plate_data_list, objects_data, export_plate_idx, m_save_gcode, m_use_loaded_id)) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", _add_model_config_file_to_archive failed\n"); return false; } + if (!_add_cut_information_file_to_archive(archive, model)) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", _add_cut_information_file_to_archive failed\n"); + return false; + } + //BBS progress point BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", before add sliced info to 3mf\n"); if (proFn) { @@ -5267,14 +5599,14 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) return true; } - bool _BBS_3MF_Exporter::_add_thumbnail_file_to_archive(mz_zip_archive& archive, const ThumbnailData& thumbnail_data, int index) + bool _BBS_3MF_Exporter::_add_thumbnail_file_to_archive(mz_zip_archive& archive, const ThumbnailData& thumbnail_data, const char* local_path, int index) { bool res = false; size_t png_size = 0; void* png_data = tdefl_write_image_to_png_file_in_memory_ex((const void*)thumbnail_data.pixels.data(), thumbnail_data.width, thumbnail_data.height, 4, &png_size, MZ_DEFAULT_COMPRESSION, 1); if (png_data != nullptr) { - std::string thumbnail_name = (boost::format("Metadata/plate_%1%.png") % (index + 1)).str(); + std::string thumbnail_name = (boost::format("%1%_%2%.png")%local_path % (index + 1)).str(); res = mz_zip_writer_add_mem(&archive, thumbnail_name.c_str(), (const void*)png_data, png_size, MZ_NO_COMPRESSION); mz_free(png_data); } @@ -5291,7 +5623,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) { bool res = false; - size_t png_size = 0; + /*size_t png_size = 0; void* png_data = tdefl_write_image_to_png_file_in_memory_ex((const void*)thumbnail_data.pixels.data(), thumbnail_data.width, thumbnail_data.height, 4, &png_size, MZ_DEFAULT_COMPRESSION, 1); if (png_data != nullptr) { std::string thumbnail_name = (boost::format(PATTERN_FILE_FORMAT) % (index + 1)).str(); @@ -5302,7 +5634,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) if (!res) { add_error("Unable to add thumbnail file to archive"); BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to add thumbnail file to archive\n"); - } + }*/ return res; } @@ -5437,6 +5769,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) stream << " xmlns:p=\"http://schemas.microsoft.com/3dmanufacturing/production/2015/06\" requiredextensions=\"p\""; stream << ">\n"; + std::string origin; std::string name; std::string user_name; std::string user_id; @@ -5465,6 +5798,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) description = model.model_info->description; copyright = model.model_info->copyright; name = model.model_info->model_name; + origin = model.model_info->origin; BOOST_LOG_TRIVIAL(trace) << "design_info, save_3mf found designer_cover = " << design_cover; } // remember to use metadata_item_map to store metadata info @@ -5476,6 +5810,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) } metadata_item_map[BBL_MODEL_NAME_TAG] = xml_escape(name); + metadata_item_map[BBL_ORIGIN_TAG] = xml_escape(origin); metadata_item_map[BBL_DESIGNER_TAG] = xml_escape(user_name); metadata_item_map[BBL_DESIGNER_USER_ID_TAG] = user_id; metadata_item_map[BBL_DESIGNER_COVER_FILE_TAG] = xml_escape(design_cover); @@ -5650,6 +5985,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) mz_zip_archive archive; mz_zip_zero_struct(&archive); mz_zip_writer_init_heap(&archive, 0, 1024 * 1024); + CNumericLocalesSetter locales_setter; _add_model_file_to_archive(object_paths[i], archive, model, objects_data2, nullptr, project); iter->second = objects_data2.begin()->second; void *ppBuf; size_t pSize; @@ -6302,7 +6638,39 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) return true; } - bool _BBS_3MF_Exporter::_add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list, const IdToObjectDataMap &objects_data, int export_plate_idx, bool save_gcode) + void _add_text_info_to_archive(std::stringstream& stream, const TextInfo& text_info) { + stream << " <" << TEXT_INFO_TAG << " "; + + stream << TEXT_ATTR << "=\"" << text_info.m_text << "\" "; + stream << FONT_NAME_ATTR << "=\"" << text_info.m_font_name << "\" "; + + stream << FONT_INDEX_ATTR << "=\"" << text_info.m_curr_font_idx << "\" "; + + stream << FONT_SIZE_ATTR << "=\"" << text_info.m_font_size << "\" "; + stream << THICKNESS_ATTR << "=\"" << text_info.m_thickness << "\" "; + stream << EMBEDED_DEPTH_ATTR << "=\"" << text_info.m_embeded_depth << "\" "; + stream << ROTATE_ANGLE_ATTR << "=\"" << text_info.m_rotate_angle << "\" "; + stream << TEXT_GAP_ATTR << "=\"" << text_info.m_text_gap << "\" "; + + stream << BOLD_ATTR << "=\"" << (text_info.m_bold ? 1 : 0) << "\" "; + stream << ITALIC_ATTR << "=\"" << (text_info.m_italic ? 1 : 0) << "\" "; + stream << SURFACE_TEXT_ATTR << "=\"" << (text_info.m_is_surface_text ? 1 : 0) << "\" "; + stream << KEEP_HORIZONTAL_ATTR << "=\"" << (text_info.m_keep_horizontal ? 1 : 0) << "\" "; + + stream << HIT_MESH_ATTR << "=\"" << text_info.m_rr.mesh_id << "\" "; + + stream << HIT_POSITION_ATTR << "=\""; + add_vec3(stream, text_info.m_rr.hit); + stream << "\" "; + + stream << HIT_NORMAL_ATTR << "=\""; + add_vec3(stream, text_info.m_rr.normal); + stream << "\" "; + + stream << "/>\n"; + } + + bool _BBS_3MF_Exporter::_add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list, const IdToObjectDataMap &objects_data, int export_plate_idx, bool save_gcode, bool use_loaded_id) { std::stringstream stream; std::map shared_meshes; @@ -6398,6 +6766,10 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) stream << " <" << METADATA_TAG << " "<< KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << volume->config.opt_serialize(key) << "\"/>\n"; } + const TextInfo &text_info = volume->get_text_info(); + if (!text_info.m_text.empty()) + _add_text_info_to_archive(stream, text_info); + //add the shared mesh logic const TriangleMesh* current_mesh = volume->mesh_ptr(); std::map::iterator mesh_iter; @@ -6464,10 +6836,20 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << THUMBNAIL_FILE_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha << thumbnail_file_in_3mf << "\"/>\n"; } - if (!plate_data->pattern_file.empty()) { + if (!plate_data->top_file.empty()) { + std::string top_file_in_3mf = (boost::format(TOP_FILE_FORMAT) % (plate_data->plate_index + 1)).str(); + stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << TOP_FILE_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha << top_file_in_3mf << "\"/>\n"; + } + + if (!plate_data->pick_file.empty()) { + std::string pick_file_in_3mf = (boost::format(PICK_FILE_FORMAT) % (plate_data->plate_index + 1)).str(); + stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << PICK_FILE_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha << pick_file_in_3mf << "\"/>\n"; + } + + /*if (!plate_data->pattern_file.empty()) { std::string pattern_file_in_3mf = (boost::format(PATTERN_FILE_FORMAT) % (plate_data->plate_index + 1)).str(); stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << PATTERN_FILE_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha << pattern_file_in_3mf << "\"/>\n"; - } + }*/ if (!plate_data->pattern_bbox_file.empty()) { std::string pattern_bbox_file_in_3mf = (boost::format(PATTERN_CONFIG_FILE_FORMAT) % (plate_data->plate_index + 1)).str(); stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << PATTERN_BBOX_FILE_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha << pattern_bbox_file_in_3mf << "\"/>\n"; @@ -6480,7 +6862,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) stream << " <" << INSTANCE_TAG << ">\n"; int obj_id = plate_data->objects_and_instances[j].first; int inst_id = plate_data->objects_and_instances[j].second; - int arrange_o = 0; + int identify_id = 0; ModelObject* obj = NULL; ModelInstance* inst = NULL; if (obj_id >= model.objects.size()) { @@ -6494,7 +6876,10 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) } else if (obj){ inst = obj->instances[inst_id]; - arrange_o = inst->arrange_order; + if (use_loaded_id && (inst->loaded_id > 0)) + identify_id = inst->loaded_id; + else + identify_id = inst->id().id; } if (m_skip_static && obj) { obj_id = obj->get_backup_id(); @@ -6505,7 +6890,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << OBJECT_ID_ATTR << "\" " << VALUE_ATTR << "=\"" << obj_id << "\"/>\n"; stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << INSTANCEID_ATTR << "\" " << VALUE_ATTR << "=\"" << inst_id << "\"/>\n"; - stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << ARRANGE_ORDER_ATTR << "\" " << VALUE_ATTR << "=\"" << arrange_o << "\"/>\n"; + stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << IDENTIFYID_ATTR << "\" " << VALUE_ATTR << "=\"" << identify_id << "\"/>\n"; stream << " \n"; } } @@ -6562,6 +6947,67 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) return true; } + bool _BBS_3MF_Exporter::_add_cut_information_file_to_archive(mz_zip_archive &archive, Model &model) + { + std::string out = ""; + pt::ptree tree; + + unsigned int object_cnt = 0; + for (const ModelObject *object : model.objects) { + object_cnt++; + pt::ptree &obj_tree = tree.add("objects.object", ""); + + obj_tree.put(".id", object_cnt); + + // Store info for cut_id + pt::ptree &cut_id_tree = obj_tree.add("cut_id", ""); + + // store cut_id atributes + cut_id_tree.put(".id", object->cut_id.id().id); + cut_id_tree.put(".check_sum", object->cut_id.check_sum()); + cut_id_tree.put(".connectors_cnt", object->cut_id.connectors_cnt()); + + int volume_idx = -1; + for (const ModelVolume *volume : object->volumes) { + ++volume_idx; + if (volume->is_cut_connector()) { + pt::ptree &connectors_tree = obj_tree.add("connectors.connector", ""); + connectors_tree.put(".volume_id", volume_idx); + connectors_tree.put(".type", int(volume->cut_info.connector_type)); + connectors_tree.put(".r_tolerance", volume->cut_info.radius_tolerance); + connectors_tree.put(".h_tolerance", volume->cut_info.height_tolerance); + } + } + } + + if (!tree.empty()) { + std::ostringstream oss; + pt::write_xml(oss, tree); + out = oss.str(); + + // Post processing("beautification") of the output string for a better preview + boost::replace_all(out, ">\n \n ", ">\n "); + boost::replace_all(out, ">\n ", ">\n "); + boost::replace_all(out, ">\n ", ">\n "); + boost::replace_all(out, ">", ">\n "); + // OR just + boost::replace_all(out, "><", ">\n<"); + } + + if (!out.empty()) { + if (!mz_zip_writer_add_mem(&archive, CUT_INFORMATION_FILE.c_str(), (const void *) out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) { + add_error("Unable to add cut information file to archive"); + return false; + } + } + + return true; + } + bool _BBS_3MF_Exporter::_add_slice_info_config_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list) { std::stringstream stream; @@ -7105,7 +7551,10 @@ public: while (true) { while (m_tasks.empty()) { - m_cond.timed_wait(lock, m_next_backup); + if (m_interval > 0) + m_cond.timed_wait(lock, m_next_backup); + else + m_cond.wait(lock); if (m_interval > 0 && boost::get_system_time() > m_next_backup) { m_tasks.push_back({ Backup, 0, std::string(), nullptr, ++m_task_seq }); m_next_backup += boost::posix_time::seconds(m_interval); diff --git a/src/libslic3r/Format/bbs_3mf.hpp b/src/libslic3r/Format/bbs_3mf.hpp index 9a09877be5..d4fea82ae3 100644 --- a/src/libslic3r/Format/bbs_3mf.hpp +++ b/src/libslic3r/Format/bbs_3mf.hpp @@ -16,7 +16,9 @@ struct ThumbnailData; #define GCODE_FILE_FORMAT "Metadata/plate_%1%.gcode" #define THUMBNAIL_FILE_FORMAT "Metadata/plate_%1%.png" -#define PATTERN_FILE_FORMAT "Metadata/plate_%1%_pattern_layer_0.png" +#define TOP_FILE_FORMAT "Metadata/top_%1%.png" +#define PICK_FILE_FORMAT "Metadata/pick_%1%.png" +//#define PATTERN_FILE_FORMAT "Metadata/plate_%1%_pattern_layer_0.png" #define PATTERN_CONFIG_FILE_FORMAT "Metadata/plate_%1%.json" #define EMBEDDED_PRINT_FILE_FORMAT "Metadata/process_settings_%1%.config" #define EMBEDDED_FILAMENT_FILE_FORMAT "Metadata/filament_settings_%1%.config" @@ -65,8 +67,10 @@ struct PlateData std::string gcode_file_md5; std::string thumbnail_file; ThumbnailData plate_thumbnail; - ThumbnailData pattern_thumbnail; - std::string pattern_file; + std::string top_file; + std::string pick_file; + //ThumbnailData pattern_thumbnail; + //std::string pattern_file; std::string pattern_bbox_file; std::string gcode_prediction; std::string gcode_weight; @@ -103,6 +107,7 @@ enum class SaveStrategy SkipModel = 1 << 7, WithSliceInfo = 1 << 8, SkipAuxiliary = 1 << 9, + UseLoadedId = 1 << 10, SplitModel = 0x1000 | ProductionExt, Encrypted = SecureContentExt | SplitModel, @@ -195,6 +200,8 @@ struct StoreParams std::vector project_presets; DynamicPrintConfig* config; std::vector thumbnail_data; + std::vector top_thumbnail_data; + std::vector pick_thumbnail_data; std::vector calibration_thumbnail_data; SaveStrategy strategy = SaveStrategy::Zip64; Export3mfProgressFn proFn = nullptr; diff --git a/src/libslic3r/Format/svg.cpp b/src/libslic3r/Format/svg.cpp index bf2f3cf492..edca80e770 100644 --- a/src/libslic3r/Format/svg.cpp +++ b/src/libslic3r/Format/svg.cpp @@ -19,6 +19,9 @@ #include "TopExp_Explorer.hxx" #include "TopoDS.hxx" #include "BRepExtrema_SelfIntersection.hxx" +#include "clipper/clipper.hpp" + +using namespace ClipperLib; namespace Slic3r { const double STEP_TRANS_CHORD_ERROR = 0.005; @@ -188,7 +191,8 @@ bool get_svg_profile(const char *path, std::vector &element_infos, } } // keep the start and end points of profile connected - profile_line_points.back().second = profile_line_points[0].first; + if (shape->fill.gradient != nullptr) + profile_line_points.back().second = profile_line_points[0].first; if (is_profile_self_interaction(profile_line_points)) BOOST_LOG_TRIVIAL(warning) << "the profile is self interaction."; @@ -196,6 +200,56 @@ bool get_svg_profile(const char *path, std::vector &element_infos, path_line_points.push_back(profile_line_points); } + if (shape->fill.gradient == nullptr) { + double scale_size = 1e6; + std::vector>> new_path_line_points; + float stroke_width = shape->strokeWidth * scale_size; + Polygons polygons; + bool close_polygon = false; + for (int i = 0; i < path_line_points.size(); ++i) { + ClipperLib::Path pt_path; + for (auto line_point : path_line_points[i]) { + pt_path.push_back(IntPoint(line_point.first.X() * scale_size, line_point.first.Y() * scale_size)); + } + pt_path.push_back(IntPoint(path_line_points[i].back().second.X() * scale_size, path_line_points[i].back().second.Y() * scale_size)); + + ClipperLib::Paths out_paths; + ClipperLib::ClipperOffset co; + if (pt_path.front() == pt_path.back()) { + co.AddPath(pt_path, ClipperLib::jtMiter, ClipperLib::etClosedLine); + close_polygon = true; + } else { + co.AddPath(pt_path, ClipperLib::jtMiter, ClipperLib::etOpenSquare); + close_polygon = false; + } + co.Execute(out_paths, stroke_width / 2); + + for (auto out_path : out_paths) { + polygons.emplace_back(Polygon(out_path)); + } + } + + if (!close_polygon) + polygons = union_(polygons); + + std::vector> profile_line_points; + for (auto polygon : polygons) { + profile_line_points.clear(); + for (int i = 0; i < polygon.size() - 1; ++i) { + gp_Pnt pt1(double(polygon[i][0] / scale_size), double(polygon[i][1] / scale_size), 0); + gp_Pnt pt2(double(polygon[i + 1][0] / scale_size), double(polygon[i + 1][1] / scale_size), 0); + profile_line_points.push_back({pt1, pt2}); + } + gp_Pnt pt1(double(polygon.back()[0] / scale_size), double(polygon.back()[1] / scale_size), 0); + gp_Pnt pt2(double(polygon.front()[0] / scale_size), double(polygon.front()[1] / scale_size), 0); + profile_line_points.push_back({pt1, pt2}); + + new_path_line_points.push_back(profile_line_points); + } + + path_line_points = new_path_line_points; + } + // generate all profile curves std::vector wires; int index = 0; diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 7c31ed482d..8d45285afe 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -529,9 +529,20 @@ static std::vector get_path_of_change_filament(const Print& print) // retract before toolchange toolchange_gcode_str = toolchange_retract_str + toolchange_gcode_str; - //BBS: current position and fan_speed is unclear after interting change_filament_gcode - toolchange_gcode_str += ";_FORCE_RESUME_FAN_SPEED\n"; - gcodegen.writer().set_current_position_clear(false); + //BBS + { + //BBS: current position and fan_speed is unclear after interting change_filament_gcode + check_add_eol(toolchange_gcode_str); + toolchange_gcode_str += ";_FORCE_RESUME_FAN_SPEED\n"; + gcodegen.writer().set_current_position_clear(false); + //BBS: check whether custom gcode changes the z position. Update if changed + double temp_z_after_tool_change; + if (GCodeProcessor::get_last_z_from_gcode(toolchange_gcode_str, temp_z_after_tool_change)) { + Vec3d pos = gcodegen.writer().get_position(); + pos(2) = temp_z_after_tool_change; + gcodegen.writer().set_position(pos); + } + } // move to start_pos for wiping after toolchange std::string start_pos_str; @@ -1791,6 +1802,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato //BBS: gcode writer doesn't know where the real position of extruder is after inserting custom gcode m_writer.set_current_position_clear(false); + m_start_gcode_filament = GCodeProcessor::get_gcode_last_filament(machine_start_gcode); // Process filament-specific gcode. /* if (has_wipe_tower) { @@ -4371,11 +4383,13 @@ bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role, LiftTyp if (role == erSupportMaterial || role == erSupportTransition) { const SupportLayer* support_layer = dynamic_cast(m_layer); //FIXME support_layer->support_islands.contains should use some search structure! - if (support_layer != NULL && support_layer->support_islands.contains(travel)) + if (support_layer != NULL) // skip retraction if this is a travel move inside a support material island //FIXME not retracting over a long path may cause oozing, which in turn may result in missing material // at the end of the extrusion path! - return false; + for (const ExPolygon& support_island : support_layer->support_islands) + if (support_island.contains(travel)) + return false; //reduce the retractions in lightning infills for tree support if (support_layer != NULL && support_layer->support_type==stInnerTree) for (auto &area : support_layer->base_areas) @@ -4384,7 +4398,7 @@ bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role, LiftTyp } //BBS: need retract when long moving to print perimeter to avoid dropping of material if (!is_perimeter(role) && m_config.reduce_infill_retraction && m_layer != nullptr && - m_config.sparse_infill_density.value > 0 && m_layer->any_internal_region_slice_contains(travel)) + m_config.sparse_infill_density.value > 0 && m_retract_when_crossing_perimeters.travel_inside_internal_regions(*m_layer, travel)) // Skip retraction if travel is contained in an internal slice *and* // internal infill is enabled (so that stringing is entirely not visible). //FIXME any_internal_region_slice_contains() is potentionally very slow, it shall test for the bounding boxes first. @@ -4485,18 +4499,26 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) float new_retract_length = m_config.retraction_length.get_at(extruder_id); float new_retract_length_toolchange = m_config.retract_length_toolchange.get_at(extruder_id); int new_filament_temp = this->on_first_layer() ? m_config.nozzle_temperature_initial_layer.get_at(extruder_id): m_config.nozzle_temperature.get_at(extruder_id); + // BBS: if print_z == 0 use first layer temperature + if (abs(print_z) < EPSILON) + new_filament_temp = m_config.nozzle_temperature_initial_layer.get_at(extruder_id); + Vec3d nozzle_pos = m_writer.get_position(); float old_retract_length, old_retract_length_toolchange, wipe_volume; int old_filament_temp, old_filament_e_feedrate; float filament_area = float((M_PI / 4.f) * pow(m_config.filament_diameter.get_at(extruder_id), 2)); - - if (m_writer.extruder() != nullptr) { + //BBS: add handling for filament change in start gcode + int previous_extruder_id = -1; + if (m_writer.extruder() != nullptr || m_start_gcode_filament != -1) { std::vector flush_matrix(cast(m_config.flush_volumes_matrix.values)); const unsigned int number_of_extruders = (unsigned int)(sqrt(flush_matrix.size()) + EPSILON); - assert(m_writer.extruder()->id() < number_of_extruders); + if (m_writer.extruder() != nullptr) + assert(m_writer.extruder()->id() < number_of_extruders); + else + assert(m_start_gcode_filament < number_of_extruders); - int previous_extruder_id = m_writer.extruder()->id(); + previous_extruder_id = m_writer.extruder() != nullptr ? m_writer.extruder()->id() : m_start_gcode_filament; old_retract_length = m_config.retraction_length.get_at(previous_extruder_id); old_retract_length_toolchange = m_config.retract_length_toolchange.get_at(previous_extruder_id); old_filament_temp = this->on_first_layer()? m_config.nozzle_temperature_initial_layer.get_at(previous_extruder_id) : m_config.nozzle_temperature.get_at(previous_extruder_id); @@ -4504,8 +4526,9 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) wipe_volume *= m_config.flush_multiplier; old_filament_e_feedrate = (int)(60.0 * m_config.filament_max_volumetric_speed.get_at(previous_extruder_id) / filament_area); old_filament_e_feedrate = old_filament_e_feedrate == 0 ? 100 : old_filament_e_feedrate; - } - else { + //BBS: must clean m_start_gcode_filament + m_start_gcode_filament = -1; + } else { old_retract_length = 0.f; old_retract_length_toolchange = 0.f; old_filament_temp = 0; @@ -4517,7 +4540,7 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) new_filament_e_feedrate = new_filament_e_feedrate == 0 ? 100 : new_filament_e_feedrate; DynamicConfig dyn_config; - dyn_config.set_key_value("previous_extruder", new ConfigOptionInt((int)(m_writer.extruder() != nullptr ? m_writer.extruder()->id() : -1))); + dyn_config.set_key_value("previous_extruder", new ConfigOptionInt(previous_extruder_id)); dyn_config.set_key_value("next_extruder", new ConfigOptionInt((int)extruder_id)); dyn_config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); dyn_config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); @@ -4566,12 +4589,23 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) std::string toolchange_gcode_parsed; if (!change_filament_gcode.empty()) { toolchange_gcode_parsed = placeholder_parser_process("change_filament_gcode", change_filament_gcode, extruder_id, &dyn_config); + check_add_eol(toolchange_gcode_parsed); gcode += toolchange_gcode_parsed; - check_add_eol(gcode); - //BBS: gcode writer doesn't know where the extruder is and whether fan speed is changed after inserting tool change gcode - //Set this flag so that normal lift will be used the first time after tool change. - gcode += ";_FORCE_RESUME_FAN_SPEED\n"; - m_writer.set_current_position_clear(false); + + //BBS + { + //BBS: gcode writer doesn't know where the extruder is and whether fan speed is changed after inserting tool change gcode + //Set this flag so that normal lift will be used the first time after tool change. + gcode += ";_FORCE_RESUME_FAN_SPEED\n"; + m_writer.set_current_position_clear(false); + //BBS: check whether custom gcode changes the z position. Update if changed + double temp_z_after_tool_change; + if (GCodeProcessor::get_last_z_from_gcode(toolchange_gcode_parsed, temp_z_after_tool_change)) { + Vec3d pos = m_writer.get_position(); + pos(2) = temp_z_after_tool_change; + m_writer.set_position(pos); + } + } } // BBS. Reset old extruder E-value. diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index d3cb7e541d..315e69b511 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -10,6 +10,7 @@ #include "PrintConfig.hpp" #include "GCode/AvoidCrossingPerimeters.hpp" #include "GCode/CoolingBuffer.hpp" +#include "GCode/RetractWhenCrossingPerimeters.hpp" #include "GCode/SpiralVase.hpp" #include "GCode/ToolOrdering.hpp" #include "GCode/WipeTower.hpp" @@ -433,6 +434,7 @@ private: OozePrevention m_ooze_prevention; Wipe m_wipe; AvoidCrossingPerimeters m_avoid_crossing_perimeters; + RetractWhenCrossingPerimeters m_retract_when_crossing_perimeters; bool m_enable_loop_clipping; // If enabled, the G-code generator will put following comments at the ends // of the G-code lines: _EXTRUDE_SET_SPEED, _WIPE, _OVERHANG_FAN_START, _OVERHANG_FAN_END @@ -494,6 +496,7 @@ private: unsigned int m_toolchange_count; coordf_t m_nominal_z; bool m_need_change_layer_lift_z = false; + int m_start_gcode_filament = -1; // BBS int get_bed_temperature(const int extruder_id, const bool is_first_layer, const BedType bed_type) const; diff --git a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp index aca9fdf6ea..4d9f3dcf1b 100644 --- a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp +++ b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp @@ -1010,7 +1010,7 @@ static ExPolygons get_boundary(const Layer &layer) ExPolygons boundary = union_ex(inner_offset(layer.lslices, 1.5 * perimeter_spacing)); if(support_layer) { #ifdef INCLUDE_SUPPORTS_IN_BOUNDARY - append(boundary, inner_offset(support_layer->support_islands.expolygons, 1.5 * perimeter_spacing)); + append(boundary, inner_offset(support_layer->support_islands, 1.5 * perimeter_spacing)); #endif auto *layer_below = layer.object()->get_first_layer_bellow_printz(layer.print_z, EPSILON); if (layer_below) @@ -1063,7 +1063,7 @@ static Polygons get_boundary_external(const Layer &layer) for (const ExPolygon &island : layer_below->lslices) append(holes_per_obj, island.holes); #ifdef INCLUDE_SUPPORTS_IN_BOUNDARY - append(supports_per_obj, support_layer->support_islands.expolygons); + append(supports_per_obj, support_layer->support_islands); #endif } diff --git a/src/libslic3r/GCode/ConflictChecker.cpp b/src/libslic3r/GCode/ConflictChecker.cpp new file mode 100644 index 0000000000..dd3d00d752 --- /dev/null +++ b/src/libslic3r/GCode/ConflictChecker.cpp @@ -0,0 +1,278 @@ +#include "ConflictChecker.hpp" + +#include +#include + +#include +#include +#include + +namespace Slic3r { + +namespace RasterizationImpl { +using IndexPair = std::pair; +using Grids = std::vector; + +inline constexpr int64_t RasteXDistance = scale_(1); +inline constexpr int64_t RasteYDistance = scale_(1); + +inline IndexPair point_map_grid_index(const Point &pt, int64_t xdist, int64_t ydist) +{ + auto x = pt.x() / xdist; + auto y = pt.y() / ydist; + return std::make_pair(x, y); +} + +inline bool nearly_equal(const Point &p1, const Point &p2) { return std::abs(p1.x() - p2.x()) < SCALED_EPSILON && std::abs(p1.y() - p2.y()) < SCALED_EPSILON; } + +inline Grids line_rasterization(const Line &line, int64_t xdist = RasteXDistance, int64_t ydist = RasteYDistance) +{ + Grids res; + Point rayStart = line.a; + Point rayEnd = line.b; + IndexPair currentVoxel = point_map_grid_index(rayStart, xdist, ydist); + IndexPair firstVoxel = currentVoxel; + IndexPair lastVoxel = point_map_grid_index(rayEnd, xdist, ydist); + + Point ray = rayEnd - rayStart; + + double stepX = ray.x() >= 0 ? 1 : -1; + double stepY = ray.y() >= 0 ? 1 : -1; + + double nextVoxelBoundaryX = (currentVoxel.first + stepX) * xdist; + double nextVoxelBoundaryY = (currentVoxel.second + stepY) * ydist; + + if (stepX < 0) { nextVoxelBoundaryX += xdist; } + if (stepY < 0) { nextVoxelBoundaryY += ydist; } + + double tMaxX = ray.x() != 0 ? (nextVoxelBoundaryX - rayStart.x()) / ray.x() : DBL_MAX; + double tMaxY = ray.y() != 0 ? (nextVoxelBoundaryY - rayStart.y()) / ray.y() : DBL_MAX; + + double tDeltaX = ray.x() != 0 ? static_cast(xdist) / ray.x() * stepX : DBL_MAX; + double tDeltaY = ray.y() != 0 ? static_cast(ydist) / ray.y() * stepY : DBL_MAX; + + res.push_back(currentVoxel); + + double tx = tMaxX; + double ty = tMaxY; + + while (lastVoxel != currentVoxel) { + if (lastVoxel.first == currentVoxel.first) { + for (int64_t i = currentVoxel.second; i != lastVoxel.second; i += (int64_t) stepY) { + currentVoxel.second += (int64_t) stepY; + res.push_back(currentVoxel); + } + break; + } + if (lastVoxel.second == currentVoxel.second) { + for (int64_t i = currentVoxel.first; i != lastVoxel.first; i += (int64_t) stepX) { + currentVoxel.first += (int64_t) stepX; + res.push_back(currentVoxel); + } + break; + } + + if (tx < ty) { + currentVoxel.first += (int64_t) stepX; + tx += tDeltaX; + } else { + currentVoxel.second += (int64_t) stepY; + ty += tDeltaY; + } + res.push_back(currentVoxel); + if (res.size() >= 100000) { // bug + assert(0); + } + } + + return res; +} +} // namespace RasterizationImpl + +void LinesBucketQueue::emplace_back_bucket(std::vector &&paths, const void *objPtr, Point offset) +{ + auto oldSize = _buckets.capacity(); + if (_objsPtrToId.find(objPtr) == _objsPtrToId.end()) { + _objsPtrToId.insert({objPtr, _objsPtrToId.size()}); + _idToObjsPtr.insert({_objsPtrToId.size() - 1, objPtr}); + } + _buckets.emplace_back(std::move(paths), _objsPtrToId[objPtr], offset); + _pq.push(&_buckets.back()); + auto newSize = _buckets.capacity(); + if (oldSize != newSize) { // pointers change + decltype(_pq) newQueue; + for (LinesBucket &bucket : _buckets) { newQueue.push(&bucket); } + std::swap(_pq, newQueue); + } +} + +double LinesBucketQueue::removeLowests() +{ + auto lowest = _pq.top(); + _pq.pop(); + double curHeight = lowest->curHeight(); + std::vector lowests; + lowests.push_back(lowest); + + while (_pq.empty() == false && std::abs(_pq.top()->curHeight() - lowest->curHeight()) < EPSILON) { + lowests.push_back(_pq.top()); + _pq.pop(); + } + + for (LinesBucket *bp : lowests) { + bp->raise(); + if (bp->valid()) { _pq.push(bp); } + } + return curHeight; +} + +LineWithIDs LinesBucketQueue::getCurLines() const +{ + LineWithIDs lines; + for (const LinesBucket &bucket : _buckets) { + if (bucket.valid()) { + LineWithIDs tmpLines = bucket.curLines(); + lines.insert(lines.end(), tmpLines.begin(), tmpLines.end()); + } + } + return lines; +} + +void getExtrusionPathsFromEntity(const ExtrusionEntityCollection *entity, ExtrusionPaths &paths) +{ + std::function getExtrusionPathImpl = [&](const ExtrusionEntityCollection *entity, ExtrusionPaths &paths) { + for (auto entityPtr : entity->entities) { + if (const ExtrusionEntityCollection *collection = dynamic_cast(entityPtr)) { + getExtrusionPathImpl(collection, paths); + } else if (const ExtrusionPath *path = dynamic_cast(entityPtr)) { + paths.push_back(*path); + } else if (const ExtrusionMultiPath *multipath = dynamic_cast(entityPtr)) { + for (const ExtrusionPath &path : multipath->paths) { paths.push_back(path); } + } else if (const ExtrusionLoop *loop = dynamic_cast(entityPtr)) { + for (const ExtrusionPath &path : loop->paths) { paths.push_back(path); } + } + } + }; + getExtrusionPathImpl(entity, paths); +} + +ExtrusionPaths getExtrusionPathsFromLayer(LayerRegionPtrs layerRegionPtrs) +{ + ExtrusionPaths paths; + for (auto regionPtr : layerRegionPtrs) { + getExtrusionPathsFromEntity(®ionPtr->perimeters, paths); + if (regionPtr->perimeters.empty() == false) { getExtrusionPathsFromEntity(®ionPtr->fills, paths); } + } + return paths; +} + +ExtrusionPaths getExtrusionPathsFromSupportLayer(SupportLayer *supportLayer) +{ + ExtrusionPaths paths; + getExtrusionPathsFromEntity(&supportLayer->support_fills, paths); + return paths; +} + +std::pair, std::vector> getAllLayersExtrusionPathsFromObject(PrintObject *obj) +{ + std::vector objPaths, supportPaths; + + for (auto layerPtr : obj->layers()) { objPaths.push_back(getExtrusionPathsFromLayer(layerPtr->regions())); } + + for (auto supportLayerPtr : obj->support_layers()) { supportPaths.push_back(getExtrusionPathsFromSupportLayer(supportLayerPtr)); } + + return {std::move(objPaths), std::move(supportPaths)}; +} + +ConflictComputeOpt ConflictChecker::find_inter_of_lines(const LineWithIDs &lines) +{ + using namespace RasterizationImpl; + std::map> indexToLine; + + for (int i = 0; i < lines.size(); ++i) { + const LineWithID &l1 = lines[i]; + auto indexes = line_rasterization(l1._line); + for (auto index : indexes) { + const auto &possibleIntersectIdxs = indexToLine[index]; + for (auto possibleIntersectIdx : possibleIntersectIdxs) { + const LineWithID &l2 = lines[possibleIntersectIdx]; + if (auto interRes = line_intersect(l1, l2); interRes.has_value()) { return interRes; } + } + indexToLine[index].push_back(i); + } + } + return {}; +} + +ConflictResultOpt ConflictChecker::find_inter_of_lines_in_diff_objs(PrintObjectPtrs objs, + std::optional wtdptr) // find the first intersection point of lines in different objects +{ + if (objs.size() <= 1) { return {}; } + LinesBucketQueue conflictQueue; + if (wtdptr.has_value()) { // wipe tower at 0 by default + auto wtpaths = wtdptr.value()->getFakeExtrusionPathsFromWipeTower(); + conflictQueue.emplace_back_bucket(std::move(wtpaths), wtdptr.value(), {wtdptr.value()->plate_origin.x(),wtdptr.value()->plate_origin.y()}); + } + for (PrintObject *obj : objs) { + auto layers = getAllLayersExtrusionPathsFromObject(obj); + conflictQueue.emplace_back_bucket(std::move(layers.first), obj, obj->instances().front().shift); + conflictQueue.emplace_back_bucket(std::move(layers.second), obj, obj->instances().front().shift); + } + + std::vector layersLines; + std::vector heights; + while (conflictQueue.valid()) { + LineWithIDs lines = conflictQueue.getCurLines(); + double curHeight = conflictQueue.removeLowests(); + heights.push_back(curHeight); + layersLines.push_back(std::move(lines)); + } + + bool find = false; + tbb::concurrent_vector> conflict; + + tbb::parallel_for(tbb::blocked_range(0, layersLines.size()), [&](tbb::blocked_range range) { + for (size_t i = range.begin(); i < range.end(); i++) { + auto interRes = find_inter_of_lines(layersLines[i]); + if (interRes.has_value()) { + find = true; + conflict.emplace_back(interRes.value(),heights[i]); + break; + } + } + }); + + if (find) { + const void *ptr1 = conflictQueue.idToObjsPtr(conflict[0].first._obj1); + const void *ptr2 = conflictQueue.idToObjsPtr(conflict[0].first._obj2); + double conflictHeight = conflict[0].second; + if (wtdptr.has_value()) { + const FakeWipeTower *wtdp = wtdptr.value(); + if (ptr1 == wtdp || ptr2 == wtdp) { + if (ptr2 == wtdp) { std::swap(ptr1, ptr2); } + const PrintObject *obj2 = reinterpret_cast(ptr2); + return std::make_optional("WipeTower", obj2->model_object()->name, conflictHeight, nullptr, ptr2); + } + } + const PrintObject *obj1 = reinterpret_cast(ptr1); + const PrintObject *obj2 = reinterpret_cast(ptr2); + return std::make_optional(obj1->model_object()->name, obj2->model_object()->name, conflictHeight, ptr1, ptr2); + } else + return {}; +} + +ConflictComputeOpt ConflictChecker::line_intersect(const LineWithID &l1, const LineWithID &l2) +{ + if (l1._id == l2._id) { return {}; } // return true if lines are from same object + Point inter; + bool intersect = l1._line.intersection(l2._line, &inter); + if (intersect) { + auto dist1 = std::min(unscale(Point(l1._line.a - inter)).norm(), unscale(Point(l1._line.b - inter)).norm()); + auto dist2 = std::min(unscale(Point(l2._line.a - inter)).norm(), unscale(Point(l2._line.b - inter)).norm()); + auto dist = std::min(dist1, dist2); + if (dist > 0.01) { return std::make_optional(l1._id, l2._id); } // the two lines intersects if dist>0.01mm + } + return {}; +} + +} // namespace Slic3r \ No newline at end of file diff --git a/src/libslic3r/GCode/ConflictChecker.hpp b/src/libslic3r/GCode/ConflictChecker.hpp new file mode 100644 index 0000000000..a9a6e85f29 --- /dev/null +++ b/src/libslic3r/GCode/ConflictChecker.hpp @@ -0,0 +1,125 @@ +#ifndef slic3r_ConflictChecker_hpp_ +#define slic3r_ConflictChecker_hpp_ + +#include "../Utils.hpp" +#include "../Model.hpp" +#include "../Print.hpp" +#include "../Layer.hpp" + +#include +#include +#include + +namespace Slic3r { + +struct LineWithID +{ + Line _line; + int _id; + int _role; + + LineWithID(const Line &line, int id, int role) : _line(line), _id(id), _role(role) {} +}; + +using LineWithIDs = std::vector; + +class LinesBucket +{ +private: + double _curHeight = 0.0; + unsigned _curPileIdx = 0; + + std::vector _piles; + int _id; + Point _offset; + +public: + LinesBucket(std::vector &&paths, int id, Point offset) : _piles(paths), _id(id), _offset(offset) {} + LinesBucket(LinesBucket &&) = default; + + bool valid() const { return _curPileIdx < _piles.size(); } + void raise() + { + if (valid()) { + if (_piles[_curPileIdx].empty() == false) { _curHeight += _piles[_curPileIdx].front().height; } + _curPileIdx++; + } + } + double curHeight() const { return _curHeight; } + LineWithIDs curLines() const + { + LineWithIDs lines; + for (const ExtrusionPath &path : _piles[_curPileIdx]) { + if (path.is_force_no_extrusion() == false) { + Polyline check_polyline = path.polyline; + check_polyline.translate(_offset); + Lines tmpLines = check_polyline.lines(); + for (const Line &line : tmpLines) { lines.emplace_back(line, _id, path.role()); } + } + } + return lines; + } + + friend bool operator>(const LinesBucket &left, const LinesBucket &right) { return left._curHeight > right._curHeight; } + friend bool operator<(const LinesBucket &left, const LinesBucket &right) { return left._curHeight < right._curHeight; } + friend bool operator==(const LinesBucket &left, const LinesBucket &right) { return left._curHeight == right._curHeight; } +}; + +struct LinesBucketPtrComp +{ + bool operator()(const LinesBucket *left, const LinesBucket *right) { return *left > *right; } +}; + +class LinesBucketQueue +{ +private: + std::vector _buckets; + std::priority_queue, LinesBucketPtrComp> _pq; + std::map _idToObjsPtr; + std::map _objsPtrToId; + +public: + void emplace_back_bucket(std::vector &&paths, const void *objPtr, Point offset); + bool valid() const { return _pq.empty() == false; } + const void *idToObjsPtr(int id) + { + if (_idToObjsPtr.find(id) != _idToObjsPtr.end()) + return _idToObjsPtr[id]; + else + return nullptr; + } + double removeLowests(); + LineWithIDs getCurLines() const; +}; + +void getExtrusionPathsFromEntity(const ExtrusionEntityCollection *entity, ExtrusionPaths &paths); + +ExtrusionPaths getExtrusionPathsFromLayer(LayerRegionPtrs layerRegionPtrs); + +ExtrusionPaths getExtrusionPathsFromSupportLayer(SupportLayer *supportLayer); + +std::pair, std::vector> getAllLayersExtrusionPathsFromObject(PrintObject *obj); + +struct ConflictComputeResult +{ + int _obj1; + int _obj2; + + ConflictComputeResult(int o1, int o2) : _obj1(o1), _obj2(o2) {} + ConflictComputeResult() = default; +}; + +using ConflictComputeOpt = std::optional; + +using ConflictObjName = std::optional>; + +struct ConflictChecker +{ + static ConflictResultOpt find_inter_of_lines_in_diff_objs(PrintObjectPtrs objs, std::optional wtdptr); + static ConflictComputeOpt find_inter_of_lines(const LineWithIDs &lines); + static ConflictComputeOpt line_intersect(const LineWithID &l1, const LineWithID &l2); +}; + +} // namespace Slic3r + +#endif diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 60da0200cd..9c6ace3487 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -12,6 +12,8 @@ #include #include +#include + #include #include #include @@ -1929,6 +1931,95 @@ template } } +int GCodeProcessor::get_gcode_last_filament(const std::string& gcode_str) +{ + int str_size = gcode_str.size(); + int start_index = 0; + int end_index = 0; + int out_filament = -1; + while (end_index < str_size) { + if (gcode_str[end_index] != '\n') { + end_index++; + continue; + } + + if (end_index > start_index) { + std::string line_str = gcode_str.substr(start_index, end_index - start_index); + line_str.erase(0, line_str.find_first_not_of(" ")); + line_str.erase(line_str.find_last_not_of(" ") + 1); + if (line_str.empty() || line_str[0] != 'T') { + start_index = end_index + 1; + end_index = start_index; + continue; + } + + int out = -1; + if (parse_number(line_str.substr(1), out) && out >= 0 && out < 255) + out_filament = out; + } + + start_index = end_index + 1; + end_index = start_index; + } + + return out_filament; +} + +//BBS: get last z position from gcode +bool GCodeProcessor::get_last_z_from_gcode(const std::string& gcode_str, double& z) +{ + int str_size = gcode_str.size(); + int start_index = 0; + int end_index = 0; + bool is_z_changed = false; + while (end_index < str_size) { + //find a full line + if (gcode_str[end_index] != '\n') { + end_index++; + continue; + } + //parse the line + if (end_index > start_index) { + std::string line_str = gcode_str.substr(start_index, end_index - start_index); + line_str.erase(0, line_str.find_first_not_of(" ")); + line_str.erase(line_str.find_last_not_of(";") + 1); + line_str.erase(line_str.find_last_not_of(" ") + 1); + + //command which may have z movement + if (line_str.size() > 5 && (line_str.find("G0 ") == 0 + || line_str.find("G1 ") == 0 + || line_str.find("G2 ") == 0 + || line_str.find("G3 ") == 0)) + { + auto z_pos = line_str.find(" Z"); + double temp_z = 0; + if (z_pos != line_str.npos + && z_pos + 2 < line_str.size()) { + // Try to parse the numeric value. + std::string z_sub = line_str.substr(z_pos + 2); + char* c = &z_sub[0]; + char* end = c + sizeof(z_sub.c_str()); + + auto is_end_of_word = [](char c) { + return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == 0 || c == ';'; + }; + + auto [pend, ec] = fast_float::from_chars(c, end, temp_z); + if (pend != c && is_end_of_word(*pend)) { + // The axis value has been parsed correctly. + z = temp_z; + is_z_changed = true; + } + } + } + } + //loop to handle next line + start_index = end_index + 1; + end_index = start_index; + } + return is_z_changed; +} + void GCodeProcessor::process_tags(const std::string_view comment, bool producers_enabled) { // producers tags diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index a064fe85c7..6fc0c31b2b 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -59,9 +59,13 @@ namespace Slic3r { time = 0.0f; prepare_time = 0.0f; custom_gcode_times.clear(); + custom_gcode_times.shrink_to_fit(); moves_times.clear(); + moves_times.shrink_to_fit(); roles_times.clear(); + roles_times.shrink_to_fit(); layers_times.clear(); + layers_times.shrink_to_fit(); } }; @@ -81,6 +85,7 @@ namespace Slic3r { m.reset(); } volumes_per_color_change.clear(); + volumes_per_color_change.shrink_to_fit(); volumes_per_extruder.clear(); flush_per_filament.clear(); used_filaments_per_role.clear(); @@ -88,8 +93,25 @@ namespace Slic3r { } }; + struct ConflictResult + { + std::string _objName1; + std::string _objName2; + double _height; + const void *_obj1; // nullptr means wipe tower + const void *_obj2; + int layer = -1; + ConflictResult(const std::string &objName1, const std::string &objName2, double height, const void *obj1, const void *obj2) + : _objName1(objName1), _objName2(objName2), _height(height), _obj1(obj1), _obj2(obj2) + {} + ConflictResult() = default; + }; + + using ConflictResultOpt = std::optional; + struct GCodeProcessorResult { + ConflictResultOpt conflict_result; struct SettingsIds { @@ -238,6 +260,9 @@ namespace Slic3r { // (the first max_count found tags are returned into found_tag) static bool contains_reserved_tags(const std::string& gcode, unsigned int max_count, std::vector& found_tag); + static int get_gcode_last_filament(const std::string &gcode_str); + static bool get_last_z_from_gcode(const std::string& gcode_str, double& z); + static const float Wipe_Width; static const float Wipe_Height; diff --git a/src/libslic3r/GCode/RetractWhenCrossingPerimeters.cpp b/src/libslic3r/GCode/RetractWhenCrossingPerimeters.cpp new file mode 100644 index 0000000000..776091adfb --- /dev/null +++ b/src/libslic3r/GCode/RetractWhenCrossingPerimeters.cpp @@ -0,0 +1,54 @@ +#include "../ClipperUtils.hpp" +#include "../Layer.hpp" +#include "../Polyline.hpp" + +#include "RetractWhenCrossingPerimeters.hpp" + +namespace Slic3r { + +bool RetractWhenCrossingPerimeters::travel_inside_internal_regions(const Layer &layer, const Polyline &travel) +{ + if (m_layer != &layer) { + // Update cache. + m_layer = &layer; + m_internal_islands.clear(); + m_aabbtree_internal_islands.clear(); + // Collect expolygons of internal slices. + for (const LayerRegion *layerm : layer.regions()) + for (const Surface &surface : layerm->get_slices().surfaces) + if (surface.is_internal()) + m_internal_islands.emplace_back(&surface.expolygon); + // Calculate bounding boxes of internal slices. + std::vector bboxes; + bboxes.reserve(m_internal_islands.size()); + for (size_t i = 0; i < m_internal_islands.size(); ++ i) + bboxes.emplace_back(i, get_extents(*m_internal_islands[i])); + // Build AABB tree over bounding boxes of internal slices. + m_aabbtree_internal_islands.build_modify_input(bboxes); + } + + BoundingBox bbox_travel = get_extents(travel); + AABBTree::BoundingBox bbox_travel_eigen{ bbox_travel.min, bbox_travel.max }; + int result = -1; + bbox_travel.offset(SCALED_EPSILON); + AABBTreeIndirect::traverse(m_aabbtree_internal_islands, + [&bbox_travel_eigen](const AABBTree::Node &node) { + return bbox_travel_eigen.intersects(node.bbox); + }, + [&travel, &bbox_travel, &result, &islands = m_internal_islands](const AABBTree::Node &node) { + assert(node.is_leaf()); + assert(node.is_valid()); + Polygons clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*islands[node.idx], bbox_travel); + if (diff_pl(travel, clipped).empty()) { + // Travel path is completely inside an "internal" island. Don't retract. + result = int(node.idx); + // Stop traversal. + return false; + } + // Continue traversal. + return true; + }); + return result != -1; +} + +} // namespace Slic3r diff --git a/src/libslic3r/GCode/RetractWhenCrossingPerimeters.hpp b/src/libslic3r/GCode/RetractWhenCrossingPerimeters.hpp new file mode 100644 index 0000000000..fb624d7f90 --- /dev/null +++ b/src/libslic3r/GCode/RetractWhenCrossingPerimeters.hpp @@ -0,0 +1,32 @@ +#ifndef slic3r_RetractWhenCrossingPerimeters_hpp_ +#define slic3r_RetractWhenCrossingPerimeters_hpp_ + +#include + +#include "../AABBTreeIndirect.hpp" + +namespace Slic3r { + +// Forward declarations. +class ExPolygon; +class Layer; +class Polyline; + +class RetractWhenCrossingPerimeters +{ +public: + bool travel_inside_internal_regions(const Layer &layer, const Polyline &travel); + +private: + // Last object layer visited, for which a cache of internal islands was created. + const Layer *m_layer; + // Internal islands only, referencing data owned by m_layer->regions()->surfaces(). + std::vector m_internal_islands; + // Search structure over internal islands. + using AABBTree = AABBTreeIndirect::Tree<2, coord_t>; + AABBTree m_aabbtree_internal_islands; +}; + +} // namespace Slic3r + +#endif // slic3r_RetractWhenCrossingPerimeters_hpp_ diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 039f95ebd4..4d474d7133 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -704,11 +704,12 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume() wipe_volumes.push_back(std::vector(flush_matrix.begin() + i * number_of_extruders, flush_matrix.begin() + (i + 1) * number_of_extruders)); unsigned int current_extruder_id = -1; - for (LayerTools& lt : m_layer_tools) { + for (int i = 0; i < m_layer_tools.size(); ++i) { + LayerTools& lt = m_layer_tools[i]; if (lt.extruders.empty()) continue; // todo: The algorithm complexity is too high(o(n2)), currently only 8 colors are supported - if (lt.extruders.size() <= 8) { + if (i != 0 && lt.extruders.size() <= 8) { lt.extruders = get_extruders_order(wipe_volumes, lt.extruders, current_extruder_id); } current_extruder_id = lt.extruders.back(); diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index 604f207157..fd51f7cf7f 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -30,6 +30,35 @@ inline float align_floor(float value, float base) return std::floor((value) / base) * base; } +static bool is_valid_gcode(const std::string &gcode) +{ + int str_size = gcode.size(); + int start_index = 0; + int end_index = 0; + bool is_valid = false; + while (end_index < str_size) { + if (gcode[end_index] != '\n') { + end_index++; + continue; + } + + if (end_index > start_index) { + std::string line_str = gcode.substr(start_index, end_index - start_index); + line_str.erase(0, line_str.find_first_not_of(" ")); + line_str.erase(line_str.find_last_not_of(" ") + 1); + if (!line_str.empty() && line_str[0] != ';') { + is_valid = true; + break; + } + } + + start_index = end_index + 1; + end_index = start_index; + } + + return is_valid; +} + class WipeTowerWriter { public: @@ -1089,7 +1118,8 @@ void WipeTower::toolchange_Wipe( x_to_wipe -= (xr - xl); if (x_to_wipe < WT_EPSILON) { - writer.travel(m_left_to_right ? xl + 1.5f*m_perimeter_width : xr - 1.5f*m_perimeter_width, writer.y(), 7200); + // BBS: Delete some unnecessary travel + //writer.travel(m_left_to_right ? xl + 1.5f*m_perimeter_width : xr - 1.5f*m_perimeter_width, writer.y(), 7200); break; } // stepping to the next line: @@ -1101,9 +1131,12 @@ void WipeTower::toolchange_Wipe( // We may be going back to the model - wipe the nozzle. If this is followed // by finish_layer, this wipe path will be overwritten. + //writer.add_wipe_point(writer.x(), writer.y()) + // .add_wipe_point(writer.x(), writer.y() - dy) + // .add_wipe_point(! m_left_to_right ? m_wipe_tower_width : 0.f, writer.y() - dy); + // BBS: modify the wipe_path after toolchange writer.add_wipe_point(writer.x(), writer.y()) - .add_wipe_point(writer.x(), writer.y() - dy) - .add_wipe_point(! m_left_to_right ? m_wipe_tower_width : 0.f, writer.y() - dy); + .add_wipe_point(! m_left_to_right ? m_wipe_tower_width : 0.f, writer.y()); if (m_layer_info != m_plan.end() && m_current_tool != m_layer_info->tool_changes.back().new_tool) m_left_to_right = !m_left_to_right; @@ -1171,13 +1204,15 @@ WipeTower::ToolChangeResult WipeTower::finish_layer(bool extrude_perimeter, bool writer.rectangle_fill_box(this, fill_box.ld, fill_box.rd.x() - fill_box.ld.x(), fill_box.ru.y() - fill_box.rd.y(), feedrate); // we are in one of the corners, travel to ld along the perimeter: - if (writer.x() > fill_box.ld.x() + EPSILON) writer.travel(fill_box.ld.x(), writer.y()); - if (writer.y() > fill_box.ld.y() + EPSILON) writer.travel(writer.x(), fill_box.ld.y()); + // BBS: Delete some unnecessary travel + //if (writer.x() > fill_box.ld.x() + EPSILON) writer.travel(fill_box.ld.x(), writer.y()); + //if (writer.y() > fill_box.ld.y() + EPSILON) writer.travel(writer.x(), fill_box.ld.y()); // Extrude infill to support the material to be printed above. const float dy = (fill_box.lu.y() - fill_box.ld.y() - m_perimeter_width); float left = fill_box.lu.x() + 2*m_perimeter_width; float right = fill_box.ru.x() - 2 * m_perimeter_width; + std::vector finish_rect_wipe_path; if (extruder_fill && dy > m_perimeter_width) { writer.travel(fill_box.ld + Vec2f(m_perimeter_width * 2, 0.f)) @@ -1225,6 +1260,9 @@ WipeTower::ToolChangeResult WipeTower::finish_layer(bool extrude_perimeter, bool writer.travel(x,writer.y()); writer.extrude(x,i%2 ? fill_box.rd.y() : fill_box.ru.y()); } + // BBS: add wipe_path for this case: only with finish rectangle + finish_rect_wipe_path.emplace_back(writer.pos()); + finish_rect_wipe_path.emplace_back(Vec2f(left + dx * n, n % 2 ? fill_box.ru.y() : fill_box.rd.y())); } writer.append("; CP EMPTY GRID END\n" @@ -1278,6 +1316,11 @@ WipeTower::ToolChangeResult WipeTower::finish_layer(bool extrude_perimeter, bool (writer.pos() == wt_box.rd ? wt_box.ru : (writer.pos() == wt_box.ru ? wt_box.lu : wt_box.ld))); + + // BBS: add wipe_path for this case: only with finish rectangle + if (finish_rect_wipe_path.size() == 2 && finish_rect_wipe_path[0] == writer.pos()) + target = finish_rect_wipe_path[1]; + writer.add_wipe_point(writer.pos()) .add_wipe_point(target); @@ -1606,7 +1649,7 @@ void WipeTower::generate(std::vector> & else { if (idx == -1) layer_result[0] = merge_tcr(finish_layer_tcr, layer_result[0]); - else + else if (is_valid_gcode(finish_layer_tcr.gcode)) layer_result[idx] = merge_tcr(layer_result[idx], finish_layer_tcr); } @@ -1641,8 +1684,9 @@ WipeTower::ToolChangeResult WipeTower::only_generate_out_wall() bool toolchanges_on_layer = m_layer_info->toolchanges_depth() > WT_EPSILON; // we are in one of the corners, travel to ld along the perimeter: - if (writer.x() > fill_box.ld.x() + EPSILON) writer.travel(fill_box.ld.x(), writer.y()); - if (writer.y() > fill_box.ld.y() + EPSILON) writer.travel(writer.x(), fill_box.ld.y()); + // BBS: Delete some unnecessary travel + //if (writer.x() > fill_box.ld.x() + EPSILON) writer.travel(fill_box.ld.x(), writer.y()); + //if (writer.y() > fill_box.ld.y() + EPSILON) writer.travel(writer.x(), fill_box.ld.y()); // outer perimeter (always): // BBS diff --git a/src/libslic3r/GCode/WipeTower.hpp b/src/libslic3r/GCode/WipeTower.hpp index 0c4175cc6b..dec8887019 100644 --- a/src/libslic3r/GCode/WipeTower.hpp +++ b/src/libslic3r/GCode/WipeTower.hpp @@ -156,6 +156,8 @@ public: float get_depth() const { return m_wipe_tower_depth; } float get_brim_width() const { return m_wipe_tower_brim_width_real; } + float get_height() const { return m_wipe_tower_height; } + float get_layer_height() const { return m_layer_height; } void set_last_layer_extruder_fill(bool extruder_fill) { if (!m_plan.empty()) { diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index d537bb2101..9388738775 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -441,20 +441,30 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co /* In all the other cases, we perform an actual XYZ move and cancel the lift. */ m_lifted = 0; - m_pos = point; } //BBS: take plate offset into consider - this->set_current_position_clear(true); Vec3d point_on_plate = { dest_point(0) - m_x_offset, dest_point(1) - m_y_offset, dest_point(2) }; - m_pos = dest_point; - + std::string out_string; GCodeG1Formatter w; - w.emit_xyz(point_on_plate); - w.emit_f(travel_speed * 60.0); - //BBS - w.emit_comment(GCodeWriter::full_gcode_comment, comment); - return w.string(); + if (!this->is_current_position_clear()) + { + //force to move xy first then z after filament change + w.emit_xy(Vec2d(point_on_plate.x(), point_on_plate.y())); + w.emit_f(this->config.travel_speed.value * 60.0); + w.emit_comment(GCodeWriter::full_gcode_comment, comment); + out_string = w.string() + _travel_to_z(point_on_plate.z(), comment); + } else { + GCodeG1Formatter w; + w.emit_xyz(point_on_plate); + w.emit_f(this->config.travel_speed.value * 60.0); + w.emit_comment(GCodeWriter::full_gcode_comment, comment); + out_string = w.string(); + } + + m_pos = dest_point; + this->set_current_position_clear(true); + return out_string; } std::string GCodeWriter::travel_to_z(double z, const std::string &comment) diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp index 3d42791413..2ae602aa2f 100644 --- a/src/libslic3r/GCodeWriter.hpp +++ b/src/libslic3r/GCodeWriter.hpp @@ -76,6 +76,7 @@ public: std::string lift(LiftType lift_type = LiftType::NormalLift); std::string unlift(); Vec3d get_position() const { return m_pos; } + void set_position(Vec3d& in) { m_pos = in; } //BBS: set offset for gcode writer void set_xy_offset(double x, double y) { m_x_offset = x; m_y_offset = y; } diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index ca92c241ea..a2c59ec17d 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -409,6 +409,20 @@ void rotation_from_two_vectors(Vec3d from, Vec3d to, Vec3d& rotation_axis, doubl } } +Transform3d translation_transform(const Vec3d &translation) +{ + Transform3d transform = Transform3d::Identity(); + transform.translate(translation); + return transform; +} + +Transform3d rotation_transform(const Vec3d& rotation) +{ + Transform3d transform = Transform3d::Identity(); + transform.rotate(Eigen::AngleAxisd(rotation.z(), Vec3d::UnitZ()) * Eigen::AngleAxisd(rotation.y(), Vec3d::UnitY()) * Eigen::AngleAxisd(rotation.x(), Vec3d::UnitX())); + return transform; +} + Transformation::Flags::Flags() : dont_translate(true) , dont_rotate(true) diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index fde3815eac..8eb6195a10 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -348,6 +348,15 @@ Vec3d extract_euler_angles(const Transform3d& transform); // Euler angles can be obtained by extract_euler_angles() void rotation_from_two_vectors(Vec3d from, Vec3d to, Vec3d& rotation_axis, double& phi, Matrix3d* rotation_matrix = nullptr); +// Returns the transform obtained by assembling the given translation +Transform3d translation_transform(const Vec3d &translation); + +// Returns the transform obtained by assembling the given rotations in the following order: +// 1) rotate X +// 2) rotate Y +// 3) rotate Z +Transform3d rotation_transform(const Vec3d &rotation); + class Transformation { struct Flags diff --git a/src/libslic3r/Geometry/ConvexHull.cpp b/src/libslic3r/Geometry/ConvexHull.cpp index b1ff77f801..b8bf5eb696 100644 --- a/src/libslic3r/Geometry/ConvexHull.cpp +++ b/src/libslic3r/Geometry/ConvexHull.cpp @@ -1,6 +1,7 @@ #include "libslic3r.h" #include "ConvexHull.hpp" #include "BoundingBox.hpp" +#include "../Geometry.hpp" #include @@ -19,13 +20,13 @@ Polygon convex_hull(Points pts) hull.points.resize(2 * n); // Build lower hull for (int i = 0; i < n; ++ i) { - while (k >= 2 && pts[i].ccw(hull[k-2], hull[k-1]) <= 0) + while (k >= 2 && Geometry::orient(pts[i], hull[k-2], hull[k-1]) != Geometry::ORIENTATION_CCW) -- k; hull[k ++] = pts[i]; } // Build upper hull for (int i = n-2, t = k+1; i >= 0; i--) { - while (k >= t && pts[i].ccw(hull[k-2], hull[k-1]) <= 0) + while (k >= t && Geometry::orient(pts[i], hull[k-2], hull[k-1]) != Geometry::ORIENTATION_CCW) -- k; hull[k ++] = pts[i]; } @@ -58,7 +59,7 @@ Pointf3s convex_hull(Pointf3s points) Point k1 = Point::new_scale(hull[k - 1](0), hull[k - 1](1)); Point k2 = Point::new_scale(hull[k - 2](0), hull[k - 2](1)); - if (p.ccw(k2, k1) <= 0) + if (Geometry::orient(p, k2, k1) != Geometry::ORIENTATION_CCW) --k; else break; @@ -76,7 +77,7 @@ Pointf3s convex_hull(Pointf3s points) Point k1 = Point::new_scale(hull[k - 1](0), hull[k - 1](1)); Point k2 = Point::new_scale(hull[k - 2](0), hull[k - 2](1)); - if (p.ccw(k2, k1) <= 0) + if (Geometry::orient(p, k2, k1) != Geometry::ORIENTATION_CCW) --k; else break; @@ -103,6 +104,29 @@ Polygon convex_hull(const Polygons &polygons) return convex_hull(std::move(pp)); } +Polygon convex_hull(const ExPolygons &expolygons) +{ + Points pp; + size_t sz = 0; + for (const auto &expoly : expolygons) + sz += expoly.contour.size(); + pp.reserve(sz); + for (const auto &expoly : expolygons) + pp.insert(pp.end(), expoly.contour.points.begin(), expoly.contour.points.end()); + return convex_hull(pp); +} + +Polygon convex_hulll(const Polylines &polylines) +{ + Points pp; + size_t sz = 0; + for (const auto &polyline : polylines) + sz += polyline.points.size(); + pp.reserve(sz); + for (const auto &polyline : polylines) + pp.insert(pp.end(), polyline.points.begin(), polyline.points.end()); + return convex_hull(pp); +} namespace rotcalip { @@ -374,7 +398,7 @@ bool inside_convex_polygon(const std::pair, std::vectorx()); assert(pt.x() == it_top->x()); - assert(it_bottom->y() <= pt.y() <= it_top->y()); + assert(it_bottom->y() <= pt.y() && pt.y() <= it_top->y()); return pt.y() >= it_bottom->y() && pt.y() <= it_top->y(); } diff --git a/src/libslic3r/Geometry/ConvexHull.hpp b/src/libslic3r/Geometry/ConvexHull.hpp index 03f00af6ae..eb0be4fe1f 100644 --- a/src/libslic3r/Geometry/ConvexHull.hpp +++ b/src/libslic3r/Geometry/ConvexHull.hpp @@ -1,14 +1,22 @@ #ifndef slic3r_Geometry_ConvexHull_hpp_ #define slic3r_Geometry_ConvexHull_hpp_ +#include + #include "../Polygon.hpp" namespace Slic3r { + +class ExPolygon; +using ExPolygons = std::vector; + namespace Geometry { Pointf3s convex_hull(Pointf3s points); Polygon convex_hull(Points points); Polygon convex_hull(const Polygons &polygons); +Polygon convex_hull(const ExPolygons &expolygons); +Polygon convex_hulll(const Polylines &polylines); // Returns true if the intersection of the two convex polygons A and B // is not an empty set. diff --git a/src/libslic3r/Geometry/MedialAxis.cpp b/src/libslic3r/Geometry/MedialAxis.cpp index 9abf524415..39677c9285 100644 --- a/src/libslic3r/Geometry/MedialAxis.cpp +++ b/src/libslic3r/Geometry/MedialAxis.cpp @@ -1,6 +1,7 @@ #include "MedialAxis.hpp" #include "clipper.hpp" +#include "VoronoiOffset.hpp" #ifdef SLIC3R_DEBUG namespace boost { namespace polygon { @@ -392,8 +393,7 @@ inline const typename VD::point_type retrieve_cell_point(const typename VD::cell } template -inline std::pair -measure_edge_thickness(const VD &vd, const typename VD::edge_type& edge, const SEGMENTS &segments) +inline std::pair measure_edge_thickness(const VD &vd, const typename VD::edge_type& edge, const SEGMENTS &segments) { typedef typename VD::coord_type T; const typename VD::point_type pa(edge.vertex0()->x(), edge.vertex0()->y()); @@ -442,15 +442,21 @@ private: const Lines &lines; }; -void -MedialAxis::build(ThickPolylines* polylines) +MedialAxis::MedialAxis(double min_width, double max_width, const ExPolygon &expolygon) : + m_expolygon(expolygon), m_lines(expolygon.lines()), m_min_width(min_width), m_max_width(max_width) +{} + +void MedialAxis::build(ThickPolylines* polylines) { - construct_voronoi(this->lines.begin(), this->lines.end(), &this->vd); + construct_voronoi(m_lines.begin(), m_lines.end(), &m_vd); + Slic3r::Voronoi::annotate_inside_outside(m_vd, m_lines); +// static constexpr double threshold_alpha = M_PI / 12.; // 30 degrees +// std::vector skeleton_edges = Slic3r::Voronoi::skeleton_edges_rough(vd, lines, threshold_alpha); /* // DEBUG: dump all Voronoi edges { - for (VD::const_edge_iterator edge = this->vd.edges().begin(); edge != this->vd.edges().end(); ++edge) { + for (VD::const_edge_iterator edge = m_vd.edges().begin(); edge != m_vd.edges().end(); ++edge) { if (edge->is_infinite()) continue; ThickPolyline polyline; @@ -462,74 +468,57 @@ MedialAxis::build(ThickPolylines* polylines) } */ - //typedef const VD::vertex_type vert_t; - typedef const VD::edge_type edge_t; - // collect valid edges (i.e. prune those not belonging to MAT) // note: this keeps twins, so it inserts twice the number of the valid edges - this->valid_edges.clear(); - { - std::set seen_edges; - for (VD::const_edge_iterator edge = this->vd.edges().begin(); edge != this->vd.edges().end(); ++edge) { - // if we only process segments representing closed loops, none if the - // infinite edges (if any) would be part of our MAT anyway - if (edge->is_secondary() || edge->is_infinite()) continue; - - // don't re-validate twins - if (seen_edges.find(&*edge) != seen_edges.end()) continue; // TODO: is this needed? - seen_edges.insert(&*edge); - seen_edges.insert(edge->twin()); - - if (!this->validate_edge(&*edge)) continue; - this->valid_edges.insert(&*edge); - this->valid_edges.insert(edge->twin()); + m_edge_data.assign(m_vd.edges().size() / 2, EdgeData{}); + for (VD::const_edge_iterator edge = m_vd.edges().begin(); edge != m_vd.edges().end(); edge += 2) + if (edge->is_primary() && edge->is_finite() && + (Voronoi::vertex_category(edge->vertex0()) == Voronoi::VertexCategory::Inside || + Voronoi::vertex_category(edge->vertex1()) == Voronoi::VertexCategory::Inside) && + this->validate_edge(&*edge)) { + // Valid skeleton edge. + this->edge_data(*edge).first.active = true; } - } - this->edges = this->valid_edges; // iterate through the valid edges to build polylines - while (!this->edges.empty()) { - const edge_t* edge = *this->edges.begin(); + ThickPolyline reverse_polyline; + for (VD::const_edge_iterator seed_edge = m_vd.edges().begin(); seed_edge != m_vd.edges().end(); seed_edge += 2) + if (EdgeData &seed_edge_data = this->edge_data(*seed_edge).first; seed_edge_data.active) { + // Mark this edge as visited. + seed_edge_data.active = false; + + // Start a polyline. + ThickPolyline polyline; + polyline.points.emplace_back(seed_edge->vertex0()->x(), seed_edge->vertex0()->y()); + polyline.points.emplace_back(seed_edge->vertex1()->x(), seed_edge->vertex1()->y()); + polyline.width.emplace_back(seed_edge_data.width_start); + polyline.width.emplace_back(seed_edge_data.width_end); + // Grow the polyline in a forward direction. + this->process_edge_neighbors(&*seed_edge, &polyline); + assert(polyline.width.size() == polyline.points.size() * 2 - 2); - // start a polyline - ThickPolyline polyline; - polyline.points.push_back(Point( edge->vertex0()->x(), edge->vertex0()->y() )); - polyline.points.push_back(Point( edge->vertex1()->x(), edge->vertex1()->y() )); - polyline.width.push_back(this->thickness[edge].first); - polyline.width.push_back(this->thickness[edge].second); + // Grow the polyline in a backward direction. + reverse_polyline.clear(); + this->process_edge_neighbors(seed_edge->twin(), &reverse_polyline); + polyline.points.insert(polyline.points.begin(), reverse_polyline.points.rbegin(), reverse_polyline.points.rend()); + polyline.width.insert(polyline.width.begin(), reverse_polyline.width.rbegin(), reverse_polyline.width.rend()); + polyline.endpoints.first = reverse_polyline.endpoints.second; + assert(polyline.width.size() == polyline.points.size() * 2 - 2); - // remove this edge and its twin from the available edges - (void)this->edges.erase(edge); - (void)this->edges.erase(edge->twin()); - - // get next points - this->process_edge_neighbors(edge, &polyline); - - // get previous points - { - ThickPolyline rpolyline; - this->process_edge_neighbors(edge->twin(), &rpolyline); - polyline.points.insert(polyline.points.begin(), rpolyline.points.rbegin(), rpolyline.points.rend()); - polyline.width.insert(polyline.width.begin(), rpolyline.width.rbegin(), rpolyline.width.rend()); - polyline.endpoints.first = rpolyline.endpoints.second; + // Prevent loop endpoints from being extended. + if (polyline.first_point() == polyline.last_point()) { + polyline.endpoints.first = false; + polyline.endpoints.second = false; + } + + // Append polyline to result. + polylines->emplace_back(std::move(polyline)); } - - assert(polyline.width.size() == polyline.points.size()*2 - 2); - - // prevent loop endpoints from being extended - if (polyline.first_point() == polyline.last_point()) { - polyline.endpoints.first = false; - polyline.endpoints.second = false; - } - - // append polyline to result - polylines->push_back(polyline); - } #ifdef SLIC3R_DEBUG { static int iRun = 0; - dump_voronoi_to_svg(this->lines, this->vd, polylines, debug_out_path("MedialAxis-%d.svg", iRun ++).c_str()); + dump_voronoi_to_svg(m_lines, m_vd, polylines, debug_out_path("MedialAxis-%d.svg", iRun ++).c_str()); printf("Thick lines: "); for (ThickPolylines::const_iterator it = polylines->begin(); it != polylines->end(); ++ it) { ThickLines lines = it->thicklines(); @@ -542,56 +531,68 @@ MedialAxis::build(ThickPolylines* polylines) #endif /* SLIC3R_DEBUG */ } -void -MedialAxis::build(Polylines* polylines) +void MedialAxis::build(Polylines* polylines) { ThickPolylines tp; this->build(&tp); - polylines->insert(polylines->end(), tp.begin(), tp.end()); + polylines->reserve(polylines->size() + tp.size()); + for (auto &pl : tp) + polylines->emplace_back(pl.points); } -void -MedialAxis::process_edge_neighbors(const VD::edge_type* edge, ThickPolyline* polyline) +void MedialAxis::process_edge_neighbors(const VD::edge_type *edge, ThickPolyline* polyline) { - while (true) { + for (;;) { // Since rot_next() works on the edge starting point but we want // to find neighbors on the ending point, we just swap edge with // its twin. - const VD::edge_type* twin = edge->twin(); + const VD::edge_type *twin = edge->twin(); // count neighbors for this edge - std::vector neighbors; - for (const VD::edge_type* neighbor = twin->rot_next(); neighbor != twin; - neighbor = neighbor->rot_next()) { - if (this->valid_edges.count(neighbor) > 0) neighbors.push_back(neighbor); - } + size_t num_neighbors = 0; + const VD::edge_type *first_neighbor = nullptr; + for (const VD::edge_type *neighbor = twin->rot_next(); neighbor != twin; neighbor = neighbor->rot_next()) + if (this->edge_data(*neighbor).first.active) { + if (num_neighbors == 0) + first_neighbor = neighbor; + ++ num_neighbors; + } // if we have a single neighbor then we can continue recursively - if (neighbors.size() == 1) { - const VD::edge_type* neighbor = neighbors.front(); - - // break if this is a closed loop - if (this->edges.count(neighbor) == 0) return; - - Point new_point(neighbor->vertex1()->x(), neighbor->vertex1()->y()); - polyline->points.push_back(new_point); - polyline->width.push_back(this->thickness[neighbor].first); - polyline->width.push_back(this->thickness[neighbor].second); - (void)this->edges.erase(neighbor); - (void)this->edges.erase(neighbor->twin()); - edge = neighbor; - } else if (neighbors.size() == 0) { + if (num_neighbors == 1) { + if (std::pair neighbor_data = this->edge_data(*first_neighbor); + neighbor_data.first.active) { + neighbor_data.first.active = false; + polyline->points.emplace_back(first_neighbor->vertex1()->x(), first_neighbor->vertex1()->y()); + if (neighbor_data.second) { + polyline->width.push_back(neighbor_data.first.width_end); + polyline->width.push_back(neighbor_data.first.width_start); + } else { + polyline->width.push_back(neighbor_data.first.width_start); + polyline->width.push_back(neighbor_data.first.width_end); + } + edge = first_neighbor; + // Continue chaining. + continue; + } + } else if (num_neighbors == 0) { polyline->endpoints.second = true; - return; } else { - // T-shaped or star-shaped joint - return; + // T-shaped or star-shaped joint } + // Stop chaining. + break; } } bool MedialAxis::validate_edge(const VD::edge_type* edge) { + auto retrieve_segment = [this](const VD::cell_type* cell) -> const Line& { return m_lines[cell->source_index()]; }; + auto retrieve_endpoint = [retrieve_segment](const VD::cell_type* cell) -> const Point& { + const Line &line = retrieve_segment(cell); + return cell->source_category() == boost::polygon::SOURCE_CATEGORY_SEGMENT_START_POINT ? line.a : line.b; + }; + // prevent overflows and detect almost-infinite edges #ifndef CLIPPERLIB_INT32 if (std::abs(edge->vertex0()->x()) > double(CLIPPER_MAX_COORD_UNSCALED) || @@ -602,32 +603,18 @@ bool MedialAxis::validate_edge(const VD::edge_type* edge) #endif // CLIPPERLIB_INT32 // construct the line representing this edge of the Voronoi diagram - const Line line( - Point( edge->vertex0()->x(), edge->vertex0()->y() ), - Point( edge->vertex1()->x(), edge->vertex1()->y() ) - ); - - // discard edge if it lies outside the supplied shape - // this could maybe be optimized (checking inclusion of the endpoints - // might give false positives as they might belong to the contour itself) - if (this->expolygon != NULL) { - if (line.a == line.b) { - // in this case, contains(line) returns a false positive - if (!this->expolygon->contains(line.a)) return false; - } else { - if (!this->expolygon->contains(line)) return false; - } - } + const Line line({ edge->vertex0()->x(), edge->vertex0()->y() }, + { edge->vertex1()->x(), edge->vertex1()->y() }); // retrieve the original line segments which generated the edge we're checking const VD::cell_type* cell_l = edge->cell(); const VD::cell_type* cell_r = edge->twin()->cell(); - const Line &segment_l = this->retrieve_segment(cell_l); - const Line &segment_r = this->retrieve_segment(cell_r); + const Line &segment_l = retrieve_segment(cell_l); + const Line &segment_r = retrieve_segment(cell_r); /* SVG svg("edge.svg"); - svg.draw(*this->expolygon); + svg.draw(m_expolygon); svg.draw(line); svg.draw(segment_l, "red"); svg.draw(segment_r, "blue"); @@ -651,62 +638,48 @@ bool MedialAxis::validate_edge(const VD::edge_type* edge) coordf_t w0 = cell_r->contains_segment() ? segment_r.distance_to(line.a)*2 - : (this->retrieve_endpoint(cell_r) - line.a).cast().norm()*2; + : (retrieve_endpoint(cell_r) - line.a).cast().norm()*2; coordf_t w1 = cell_l->contains_segment() ? segment_l.distance_to(line.b)*2 - : (this->retrieve_endpoint(cell_l) - line.b).cast().norm()*2; + : (retrieve_endpoint(cell_l) - line.b).cast().norm()*2; if (cell_l->contains_segment() && cell_r->contains_segment()) { // calculate the relative angle between the two boundary segments double angle = fabs(segment_r.orientation() - segment_l.orientation()); - if (angle > PI) angle = 2*PI - angle; + if (angle > PI) + angle = 2. * PI - angle; assert(angle >= 0 && angle <= PI); - + // fabs(angle) ranges from 0 (collinear, same direction) to PI (collinear, opposite direction) // we're interested only in segments close to the second case (facing segments) // so we allow some tolerance. // this filter ensures that we're dealing with a narrow/oriented area (longer than thick) // we don't run it on edges not generated by two segments (thus generated by one segment // and the endpoint of another segment), since their orientation would not be meaningful - if (PI - angle > PI/8) { + if (PI - angle > PI / 8.) { // angle is not narrow enough - // only apply this filter to segments that are not too short otherwise their // angle could possibly be not meaningful - if (w0 < SCALED_EPSILON || w1 < SCALED_EPSILON || line.length() >= this->min_width) + if (w0 < SCALED_EPSILON || w1 < SCALED_EPSILON || line.length() >= m_min_width) return false; } } else { if (w0 < SCALED_EPSILON || w1 < SCALED_EPSILON) return false; } - //BBS - if (w0 < this->min_width || w1 < this->min_width) - return false; - //BBS - if (w0 > this->max_width || w1 > this->max_width) - return false; - this->thickness[edge] = std::make_pair(w0, w1); - this->thickness[edge->twin()] = std::make_pair(w1, w0); - - return true; -} - -const Line& MedialAxis::retrieve_segment(const VD::cell_type* cell) const -{ - return this->lines[cell->source_index()]; -} - -const Point& MedialAxis::retrieve_endpoint(const VD::cell_type* cell) const -{ - const Line& line = this->retrieve_segment(cell); - if (cell->source_category() == boost::polygon::SOURCE_CATEGORY_SEGMENT_START_POINT) { - return line.a; - } else { - return line.b; + if ((w0 >= m_min_width || w1 >= m_min_width) && + (w0 <= m_max_width || w1 <= m_max_width)) { + std::pair ed = this->edge_data(*edge); + if (ed.second) + std::swap(w0, w1); + ed.first.width_start = w0; + ed.first.width_end = w1; + return true; } + + return false; } } } // namespace Slicer::Geometry diff --git a/src/libslic3r/Geometry/MedialAxis.hpp b/src/libslic3r/Geometry/MedialAxis.hpp index cfbb5f0805..b1354ddb2f 100644 --- a/src/libslic3r/Geometry/MedialAxis.hpp +++ b/src/libslic3r/Geometry/MedialAxis.hpp @@ -4,30 +4,43 @@ #include "Voronoi.hpp" #include "../ExPolygon.hpp" -namespace Slic3r { namespace Geometry { +namespace Slic3r::Geometry { class MedialAxis { public: - Lines lines; - const ExPolygon* expolygon; - double max_width; - double min_width; - MedialAxis(double _max_width, double _min_width, const ExPolygon* _expolygon = NULL) - : expolygon(_expolygon), max_width(_max_width), min_width(_min_width) {}; + MedialAxis(double min_width, double max_width, const ExPolygon &expolygon); void build(ThickPolylines* polylines); void build(Polylines* polylines); private: + // Input + const ExPolygon &m_expolygon; + Lines m_lines; + // for filtering of the skeleton edges + double m_min_width; + double m_max_width; + + // Voronoi Diagram. using VD = VoronoiDiagram; - VD vd; - std::set edges, valid_edges; - std::map > thickness; + VD m_vd; + + // Annotations of the VD skeleton edges. + struct EdgeData { + bool active { false }; + double width_start { 0 }; + double width_end { 0 }; + }; + // Returns a reference to EdgeData and a "reversed" boolean. + std::pair edge_data(const VD::edge_type &edge) { + size_t edge_id = &edge - &m_vd.edges().front(); + return { m_edge_data[edge_id / 2], (edge_id & 1) != 0 }; + } + std::vector m_edge_data; + void process_edge_neighbors(const VD::edge_type* edge, ThickPolyline* polyline); bool validate_edge(const VD::edge_type* edge); - const Line& retrieve_segment(const VD::cell_type* cell) const; - const Point& retrieve_endpoint(const VD::cell_type* cell) const; }; -} } // namespace Slicer::Geometry +} // namespace Slicer::Geometry #endif // slic3r_Geometry_MedialAxis_hpp_ diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 68bdfc8692..331129ba45 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -5,9 +5,11 @@ #include "Flow.hpp" #include "SurfaceCollection.hpp" #include "ExtrusionEntityCollection.hpp" -#include "ExPolygonCollection.hpp" namespace Slic3r { + +class ExPolygon; +using ExPolygons = std::vector; class Layer; using LayerPtrs = std::vector; class LayerRegion; @@ -30,6 +32,8 @@ public: const Layer* layer() const { return m_layer; } const PrintRegion& region() const { return *m_region; } + const SurfaceCollection& get_slices() const { return slices; } + // collection of surfaces generated by slicing the original geometry // divided by type top/bottom/internal SurfaceCollection slices; @@ -193,7 +197,7 @@ public: protected: friend class PrintObject; friend std::vector new_layers(PrintObject*, const std::vector&); - friend std::string fix_slicing_errors(PrintObject* object, LayerPtrs&, const std::function&); + friend std::string fix_slicing_errors(PrintObject* object, LayerPtrs&, const std::function&, int &); Layer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, coordf_t slice_z) : upper_layer(nullptr), lower_layer(nullptr), slicing_errors(false), @@ -224,7 +228,7 @@ class SupportLayer : public Layer public: // Polygons covered by the supports: base, interface and contact areas. // Used to suppress retraction if moving for a support extrusion over these support_islands. - ExPolygonCollection support_islands; + ExPolygons support_islands; // Extrusion paths for the support base and for the support interface and contacts. ExtrusionEntityCollection support_fills; SupportInnerType support_type = stInnerNormal; @@ -265,6 +269,7 @@ protected: ExPolygon *area; int type; coordf_t dist_to_top; // mm dist to top + bool need_infill = false; AreaGroup(ExPolygon *a, int t, coordf_t d) : area(a), type(t), dist_to_top(d) {} }; enum OverhangType { Detected = 0, Enforced }; diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 55c87d05bc..06b632eaa2 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -311,6 +311,15 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly // would get merged into a single one while they need different directions // also, supply the original expolygon instead of the grown one, because in case // of very thin (but still working) anchors, the grown expolygon would go beyond them + double custom_angle = Geometry::deg2rad(this->region().config().bridge_angle.value); + if (custom_angle > 0.0) { + bridges[idx_last].bridge_angle = custom_angle; + } else { + auto [bridging_dir, unsupported_dist] = detect_bridging_direction(to_polygons(initial), to_polygons(lower_layer->lslices)); + bridges[idx_last].bridge_angle = PI + std::atan2(bridging_dir.y(), bridging_dir.x()); + } + + /* BridgeDetector bd(initial, lower_layer->lslices, this->bridging_flow(frInfill, object_config.thick_bridges).scaled_width()); #ifdef SLIC3R_DEBUG printf("Processing bridge at layer %zu:\n", this->layer()->id()); @@ -330,6 +339,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly // using a bridging flow, therefore it makes sense to respect the custom bridging direction. bridges[idx_last].bridge_angle = custom_angle; } + */ // without safety offset, artifacts are generated (GH #2494) surfaces_append(bottom, union_safety_offset_ex(grown), bridges[idx_last]); } diff --git a/src/libslic3r/Line.cpp b/src/libslic3r/Line.cpp index 3a180f7478..7e75d56322 100644 --- a/src/libslic3r/Line.cpp +++ b/src/libslic3r/Line.cpp @@ -29,7 +29,14 @@ bool Line::intersection_infinite(const Line &other, Point* point) const if (std::fabs(denom) < EPSILON) return false; double t1 = cross2(v12, v2) / denom; - *point = (a1 + t1 * v1).cast(); + Vec2d result = (a1 + t1 * v1); + if (result.x() > std::numeric_limits::max() || result.x() < std::numeric_limits::lowest() || + result.y() > std::numeric_limits::max() || result.y() < std::numeric_limits::lowest()) { + // Intersection has at least one of the coordinates much bigger (or smaller) than coord_t maximum value (or minimum). + // So it can not be stored into the Point without integer overflows. That could mean that input lines are parallel or near parallel. + return false; + } + *point = (result).cast(); return true; } @@ -84,28 +91,7 @@ bool Line::perpendicular_to(const Line& line) const bool Line::intersection(const Line &l2, Point *intersection) const { - const Line &l1 = *this; - const Vec2d v1 = (l1.b - l1.a).cast(); - const Vec2d v2 = (l2.b - l2.a).cast(); - double denom = cross2(v1, v2); - if (fabs(denom) < EPSILON) -#if 0 - // Lines are collinear. Return true if they are coincident (overlappign). - return ! (fabs(nume_a) < EPSILON && fabs(nume_b) < EPSILON); -#else - return false; -#endif - const Vec2d v12 = (l1.a - l2.a).cast(); - double nume_a = cross2(v2, v12); - double nume_b = cross2(v1, v12); - double t1 = nume_a / denom; - double t2 = nume_b / denom; - if (t1 >= 0 && t1 <= 1.0f && t2 >= 0 && t2 <= 1.0f) { - // Get the intersection point. - (*intersection) = (l1.a.cast() + t1 * v1).cast(); - return true; - } - return false; // not intersecting + return line_alg::intersection(*this, l2, intersection); } bool Line::clip_with_bbox(const BoundingBox &bbox) diff --git a/src/libslic3r/Line.hpp b/src/libslic3r/Line.hpp index ecd1b9cb74..dbb5ff8ee0 100644 --- a/src/libslic3r/Line.hpp +++ b/src/libslic3r/Line.hpp @@ -1,8 +1,8 @@ #ifndef slic3r_Line_hpp_ #define slic3r_Line_hpp_ -#include "Point.hpp" #include "libslic3r.h" +#include "Point.hpp" #include @@ -22,201 +22,175 @@ Linef3 transform(const Linef3& line, const Transform3d& t); namespace line_alg { - template - struct Traits { - static constexpr int Dim = L::Dim; - using Scalar = typename L::Scalar; +template struct Traits { + static constexpr int Dim = L::Dim; + using Scalar = typename L::Scalar; - static Vec& get_a(L& l) { return l.a; } - static Vec& get_b(L& l) { return l.b; } - static const Vec& get_a(const L& l) { return l.a; } - static const Vec& get_b(const L& l) { return l.b; } - }; + static Vec& get_a(L &l) { return l.a; } + static Vec& get_b(L &l) { return l.b; } + static const Vec& get_a(const L &l) { return l.a; } + static const Vec& get_b(const L &l) { return l.b; } +}; - template - const constexpr int Dim = Traits>::Dim; - template - using Scalar = typename Traits>::Scalar; +template const constexpr int Dim = Traits>::Dim; +template using Scalar = typename Traits>::Scalar; - template - auto get_a(L&& l) { return Traits>::get_a(l); } - template - auto get_b(L&& l) { return Traits>::get_b(l); } +template auto get_a(L &&l) { return Traits>::get_a(l); } +template auto get_b(L &&l) { return Traits>::get_b(l); } - // Distance to the closest point of line. - template - double distance_to_squared(const L& line, const Vec, Scalar>& point, Vec, Scalar>* nearest_point) - { - const Vec, double> v = (get_b(line) - get_a(line)).template cast(); - const Vec, double> va = (point - get_a(line)).template cast(); - const double l2 = v.squaredNorm(); // avoid a sqrt - if (l2 == 0.0) { - // a == b case - *nearest_point = get_a(line); - return va.squaredNorm(); - } - // Consider the line extending the segment, parameterized as a + t (b - a). - // We find projection of this point onto the line. - // It falls where t = [(this-a) . (b-a)] / |b-a|^2 - const double t = va.dot(v) / l2; - if (t <= 0.0) { - // beyond the 'a' end of the segment - *nearest_point = get_a(line); - return va.squaredNorm(); - } else if (t >= 1.0) { - // beyond the 'b' end of the segment - *nearest_point = get_b(line); - return (point - get_b(line)).template cast().squaredNorm(); - } - - *nearest_point = (get_a(line).template cast() + t * v).template cast>(); - return (t * v - va).squaredNorm(); +// Distance to the closest point of line. +template +double distance_to_squared(const L &line, const Vec, Scalar> &point, Vec, Scalar> *nearest_point) +{ + const Vec, double> v = (get_b(line) - get_a(line)).template cast(); + const Vec, double> va = (point - get_a(line)).template cast(); + const double l2 = v.squaredNorm(); // avoid a sqrt + if (l2 == 0.0) { + // a == b case + *nearest_point = get_a(line); + return va.squaredNorm(); + } + // Consider the line extending the segment, parameterized as a + t (b - a). + // We find projection of this point onto the line. + // It falls where t = [(this-a) . (b-a)] / |b-a|^2 + const double t = va.dot(v) / l2; + if (t <= 0.0) { + // beyond the 'a' end of the segment + *nearest_point = get_a(line); + return va.squaredNorm(); + } else if (t >= 1.0) { + // beyond the 'b' end of the segment + *nearest_point = get_b(line); + return (point - get_b(line)).template cast().squaredNorm(); } - // Distance to the closest point of line. - template - double distance_to_squared(const L& line, const Vec, Scalar>& point) - { - Vec, Scalar> nearest_point; - return distance_to_squared(line, point, &nearest_point); - } + *nearest_point = (get_a(line).template cast() + t * v).template cast>(); + return (t * v - va).squaredNorm(); +} - template - double distance_to(const L& line, const Vec, Scalar>& point) - { - return std::sqrt(distance_to_squared(line, point)); - } +// Distance to the closest point of line. +template +double distance_to_squared(const L &line, const Vec, Scalar> &point) +{ + Vec, Scalar> nearest_point; + return distance_to_squared(line, point, &nearest_point); +} - // Returns a squared distance to the closest point on the infinite. - // Returned nearest_point (and returned squared distance to this point) could be beyond the 'a' and 'b' ends of the segment. - template - double distance_to_infinite_squared(const L& line, const Vec, Scalar>& point, Vec, Scalar>* closest_point) - { - const Vec, double> v = (get_b(line) - get_a(line)).template cast(); - const Vec, double> va = (point - get_a(line)).template cast(); - const double l2 = v.squaredNorm(); // avoid a sqrt - if (l2 == 0.) { - // a == b case - *closest_point = get_a(line); - return va.squaredNorm(); - } - // Consider the line extending the segment, parameterized as a + t (b - a). - // We find projection of this point onto the line. - // It falls where t = [(this-a) . (b-a)] / |b-a|^2 - const double t = va.dot(v) / l2; - *closest_point = (get_a(line).template cast() + t * v).template cast>(); - return (t * v - va).squaredNorm(); - } +template +double distance_to(const L &line, const Vec, Scalar> &point) +{ + return std::sqrt(distance_to_squared(line, point)); +} - // Returns a squared distance to the closest point on the infinite. - // Closest point (and returned squared distance to this point) could be beyond the 'a' and 'b' ends of the segment. - template - double distance_to_infinite_squared(const L& line, const Vec, Scalar>& point) - { - Vec, Scalar> nearest_point; - return distance_to_infinite_squared(line, point, &nearest_point); +// Returns a squared distance to the closest point on the infinite. +// Returned nearest_point (and returned squared distance to this point) could be beyond the 'a' and 'b' ends of the segment. +template +double distance_to_infinite_squared(const L &line, const Vec, Scalar> &point, Vec, Scalar> *closest_point) +{ + const Vec, double> v = (get_b(line) - get_a(line)).template cast(); + const Vec, double> va = (point - get_a(line)).template cast(); + const double l2 = v.squaredNorm(); // avoid a sqrt + if (l2 == 0.) { + // a == b case + *closest_point = get_a(line); + return va.squaredNorm(); } + // Consider the line extending the segment, parameterized as a + t (b - a). + // We find projection of this point onto the line. + // It falls where t = [(this-a) . (b-a)] / |b-a|^2 + const double t = va.dot(v) / l2; + *closest_point = (get_a(line).template cast() + t * v).template cast>(); + return (t * v - va).squaredNorm(); +} - // Returns a distance to the closest point on the infinite. - // Closest point (and returned squared distance to this point) could be beyond the 'a' and 'b' ends of the segment. - template - double distance_to_infinite(const L& line, const Vec, Scalar>& point) - { - return std::sqrt(distance_to_infinite_squared(line, point)); - } +// Returns a squared distance to the closest point on the infinite. +// Closest point (and returned squared distance to this point) could be beyond the 'a' and 'b' ends of the segment. +template +double distance_to_infinite_squared(const L &line, const Vec, Scalar> &point) +{ + Vec, Scalar> nearest_point; + return distance_to_infinite_squared(line, point, &nearest_point); +} - template - bool intersection(const L& l1, const L& l2, Vec, Scalar>* intersection_pt) - { - using Floating = typename std::conditional>::value, Scalar, double>::type; - using VecType = const Vec, Floating>; - const VecType v1 = (l1.b - l1.a).template cast(); - const VecType v2 = (l2.b - l2.a).template cast(); - Floating denom = cross2(v1, v2); - if (fabs(denom) < EPSILON) +// Returns a distance to the closest point on the infinite. +// Closest point (and returned squared distance to this point) could be beyond the 'a' and 'b' ends of the segment. +template +double distance_to_infinite(const L &line, const Vec, Scalar> &point) +{ + return std::sqrt(distance_to_infinite_squared(line, point)); +} + +template bool intersection(const L &l1, const L &l2, Vec, Scalar> *intersection_pt) +{ + using Floating = typename std::conditional>::value, Scalar, double>::type; + using VecType = const Vec, Floating>; + const VecType v1 = (l1.b - l1.a).template cast(); + const VecType v2 = (l2.b - l2.a).template cast(); + Floating denom = cross2(v1, v2); + if (fabs(denom) < EPSILON) #if 0 // Lines are collinear. Return true if they are coincident (overlappign). return ! (fabs(nume_a) < EPSILON && fabs(nume_b) < EPSILON); #else - return false; + return false; #endif - const VecType v12 = (l1.a - l2.a).template cast(); - Floating nume_a = cross2(v2, v12); - Floating nume_b = cross2(v1, v12); - Floating t1 = nume_a / denom; - Floating t2 = nume_b / denom; - if (t1 >= 0 && t1 <= 1.0f && t2 >= 0 && t2 <= 1.0f) { - // Get the intersection point. - (*intersection_pt) = (l1.a.template cast() + t1 * v1).template cast>(); - return true; - } - return false; // not intersecting + const VecType v12 = (l1.a - l2.a).template cast(); + Floating nume_a = cross2(v2, v12); + Floating nume_b = cross2(v1, v12); + Floating t1 = nume_a / denom; + Floating t2 = nume_b / denom; + if (t1 >= 0 && t1 <= 1.0f && t2 >= 0 && t2 <= 1.0f) { + // Get the intersection point. + (*intersection_pt) = (l1.a.template cast() + t1 * v1).template cast>(); + return true; } + return false; // not intersecting +} } // namespace line_alg -class Line { +class Line +{ public: - Line() { } - Line(const Point& _a, const Point& _b) - : a(_a) - , b(_b) - { - } - explicit operator Lines() const - { - Lines lines; - lines.emplace_back(*this); - return lines; - } - void scale(double factor) - { - this->a *= factor; - this->b *= factor; - } - void translate(const Point& v) - { - this->a += v; - this->b += v; - } - void translate(double x, double y) { this->translate(Point(x, y)); } - void rotate(double angle, const Point& center) - { - this->a.rotate(angle, center); - this->b.rotate(angle, center); - } - void reverse() { std::swap(this->a, this->b); } + Line() {} + Line(const Point& _a, const Point& _b) : a(_a), b(_b) {} + explicit operator Lines() const { Lines lines; lines.emplace_back(*this); return lines; } + void scale(double factor) { this->a *= factor; this->b *= factor; } + void translate(const Point &v) { this->a += v; this->b += v; } + void translate(double x, double y) { this->translate(Point(x, y)); } + void rotate(double angle, const Point ¢er) { this->a.rotate(angle, center); this->b.rotate(angle, center); } + void reverse() { std::swap(this->a, this->b); } double length() const { return (b - a).cast().norm(); } - Point midpoint() const { return (this->a + this->b) / 2; } - bool intersection_infinite(const Line& other, Point* point) const; - bool operator==(const Line& rhs) const { return this->a == rhs.a && this->b == rhs.b; } - double distance_to_squared(const Point& point) const { return distance_to_squared(point, this->a, this->b); } - double distance_to_squared(const Point& point, Point* closest_point) const { return line_alg::distance_to_squared(*this, point, closest_point); } - double distance_to(const Point& point) const { return distance_to(point, this->a, this->b); } - double distance_to_infinite_squared(const Point& point, Point* closest_point) const { return line_alg::distance_to_infinite_squared(*this, point, closest_point); } - double perp_distance_to(const Point& point) const; - bool parallel_to(double angle) const; - bool parallel_to(const Line& line) const; - bool perpendicular_to(double angle) const; - bool perpendicular_to(const Line& line) const; + Point midpoint() const { return (this->a + this->b) / 2; } + bool intersection_infinite(const Line &other, Point* point) const; + bool operator==(const Line &rhs) const { return this->a == rhs.a && this->b == rhs.b; } + double distance_to_squared(const Point &point) const { return distance_to_squared(point, this->a, this->b); } + double distance_to_squared(const Point &point, Point *closest_point) const { return line_alg::distance_to_squared(*this, point, closest_point); } + double distance_to(const Point &point) const { return distance_to(point, this->a, this->b); } + double distance_to_infinite_squared(const Point &point, Point *closest_point) const { return line_alg::distance_to_infinite_squared(*this, point, closest_point); } + double perp_distance_to(const Point &point) const; + bool parallel_to(double angle) const; + bool parallel_to(const Line& line) const; + bool perpendicular_to(double angle) const; + bool perpendicular_to(const Line& line) const; double atan2_() const { return atan2(this->b(1) - this->a(1), this->b(0) - this->a(0)); } double orientation() const; double direction() const; Vector vector() const { return this->b - this->a; } Vector normal() const { return Vector((this->b(1) - this->a(1)), -(this->b(0) - this->a(0))); } - bool intersection(const Line& line, Point* intersection) const; + bool intersection(const Line& line, Point* intersection) const; // Clip a line with a bounding box. Returns false if the line is completely outside of the bounding box. - bool clip_with_bbox(const BoundingBox& bbox); + bool clip_with_bbox(const BoundingBox &bbox); // Extend the line from both sides by an offset. - void extend(double offset); + void extend(double offset); - static inline double distance_to_squared(const Point& point, const Point& a, const Point& b) { return line_alg::distance_to_squared(Line { a, b }, Vec<2, coord_t> { point }); } - static double distance_to(const Point& point, const Point& a, const Point& b) { return sqrt(distance_to_squared(point, a, b)); } + static inline double distance_to_squared(const Point &point, const Point &a, const Point &b) { return line_alg::distance_to_squared(Line{a, b}, Vec<2, coord_t>{point}); } + static double distance_to(const Point &point, const Point &a, const Point &b) { return sqrt(distance_to_squared(point, a, b)); } // Returns a distance to the closest point on the infinite. // Closest point (and returned squared distance to this point) could be beyond the 'a' and 'b' ends of the segment. - static inline double distance_to_infinite_squared(const Point& point, const Point& a, const Point& b) { return line_alg::distance_to_infinite_squared(Line { a, b }, Vec<2, coord_t> { point }); } - static double distance_to_infinite(const Point& point, const Point& a, const Point& b) { return sqrt(distance_to_infinite_squared(point, a, b)); } + static inline double distance_to_infinite_squared(const Point &point, const Point &a, const Point &b) { return line_alg::distance_to_infinite_squared(Line{a, b}, Vec<2, coord_t>{point}); } + static double distance_to_infinite(const Point &point, const Point &a, const Point &b) { return sqrt(distance_to_infinite_squared(point, a, b)); } Point a; Point b; @@ -225,43 +199,23 @@ public: using Scalar = Point::Scalar; }; -class ThickLine : public Line { +class ThickLine : public Line +{ public: - ThickLine() - : a_width(0) - , b_width(0) - { - } - ThickLine(const Point& a, const Point& b) - : Line(a, b) - , a_width(0) - , b_width(0) - { - } - ThickLine(const Point& a, const Point& b, double wa, double wb) - : Line(a, b) - , a_width(wa) - , b_width(wb) - { - } + ThickLine() : a_width(0), b_width(0) {} + ThickLine(const Point& a, const Point& b) : Line(a, b), a_width(0), b_width(0) {} + ThickLine(const Point& a, const Point& b, double wa, double wb) : Line(a, b), a_width(wa), b_width(wb) {} double a_width, b_width; }; -class Line3 { +class Line3 +{ public: - Line3() - : a(Vec3crd::Zero()) - , b(Vec3crd::Zero()) - { - } - Line3(const Vec3crd& _a, const Vec3crd& _b) - : a(_a) - , b(_b) - { - } + Line3() : a(Vec3crd::Zero()), b(Vec3crd::Zero()) {} + Line3(const Vec3crd& _a, const Vec3crd& _b) : a(_a), b(_b) {} - double length() const { return (this->a - this->b).cast().norm(); } + double length() const { return (this->a - this->b).cast().norm(); } Vec3crd vector() const { return this->b - this->a; } Vec3crd a; @@ -271,18 +225,11 @@ public: using Scalar = Vec3crd::Scalar; }; -class Linef { +class Linef +{ public: - Linef() - : a(Vec2d::Zero()) - , b(Vec2d::Zero()) - { - } - Linef(const Vec2d& _a, const Vec2d& _b) - : a(_a) - , b(_b) - { - } + Linef() : a(Vec2d::Zero()), b(Vec2d::Zero()) {} + Linef(const Vec2d& _a, const Vec2d& _b) : a(_a), b(_b) {} Vec2d a; Vec2d b; @@ -292,28 +239,17 @@ public: }; using Linesf = std::vector; -class Linef3 { +class Linef3 +{ public: - Linef3() - : a(Vec3d::Zero()) - , b(Vec3d::Zero()) - { - } - Linef3(const Vec3d& _a, const Vec3d& _b) - : a(_a) - , b(_b) - { - } + Linef3() : a(Vec3d::Zero()), b(Vec3d::Zero()) {} + Linef3(const Vec3d& _a, const Vec3d& _b) : a(_a), b(_b) {} - Vec3d intersect_plane(double z) const; - void scale(double factor) - { - this->a *= factor; - this->b *= factor; - } - Vec3d vector() const { return this->b - this->a; } - Vec3d unit_vector() const { return (length() == 0.0) ? Vec3d::Zero() : vector().normalized(); } - double length() const { return vector().norm(); } + Vec3d intersect_plane(double z) const; + void scale(double factor) { this->a *= factor; this->b *= factor; } + Vec3d vector() const { return this->b - this->a; } + Vec3d unit_vector() const { return (length() == 0.0) ? Vec3d::Zero() : vector().normalized(); } + double length() const { return vector().norm(); } Vec3d a; Vec3d b; @@ -322,31 +258,26 @@ public: using Scalar = Vec3d::Scalar; }; -BoundingBox get_extents(const Lines& lines); +BoundingBox get_extents(const Lines &lines); } // namespace Slic3r // start Boost #include -namespace boost { -namespace polygon { +namespace boost { namespace polygon { template <> - struct geometry_concept { - typedef segment_concept type; - }; + struct geometry_concept { typedef segment_concept type; }; template <> struct segment_traits { typedef coord_t coordinate_type; typedef Slic3r::Point point_type; - - static inline point_type get(const Slic3r::Line& line, direction_1d dir) - { + + static inline point_type get(const Slic3r::Line& line, direction_1d dir) { return dir.to_int() ? line.b : line.a; } }; -} -} +} } // end Boost -#endif // slic3r_Line_hpp_ +#endif // slic3r_Line_hpp_ \ No newline at end of file diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index e26c136b18..bd68d7dcb2 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -70,6 +70,7 @@ Model& Model::assign_copy(const Model &rhs) // BBS: for design info this->design_info = rhs.design_info; this->model_info = rhs.model_info; + this->profile_info = rhs.profile_info; return *this; } @@ -104,6 +105,8 @@ Model& Model::assign_copy(Model &&rhs) rhs.design_info.reset(); this->model_info = rhs.model_info; rhs.model_info.reset(); + this->profile_info = rhs.profile_info; + rhs.profile_info.reset(); return *this; } @@ -667,8 +670,21 @@ bool Model::looks_like_imperial_units() const return false; for (ModelObject* obj : this->objects) - if (obj->get_object_stl_stats().volume < volume_threshold_inches) - return true; + if (obj->get_object_stl_stats().volume < volume_threshold_inches) { + if (!obj->is_cut()) + return true; + bool all_cut_parts_look_like_imperial_units = true; + for (ModelObject* obj_other : this->objects) { + if (obj_other == obj) + continue; + if (obj_other->cut_id.is_equal(obj->cut_id) && obj_other->get_object_stl_stats().volume >= volume_threshold_inches) { + all_cut_parts_look_like_imperial_units = false; + break; + } + } + if (all_cut_parts_look_like_imperial_units) + return true; + } return false; } @@ -790,9 +806,11 @@ std::string Model::get_backup_path() buf << this->id().id; backup_path = parent_path.string() + buf.str(); + BOOST_LOG_TRIVIAL(info) << boost::format("model %1%, id %2%, backup_path empty, set to %3%")%this%this->id().id%backup_path; boost::filesystem::path temp_path(backup_path); if (boost::filesystem::exists(temp_path)) { + BOOST_LOG_TRIVIAL(info) << boost::format("model %1%, id %2%, remove previous %3%")%this%this->id().id%backup_path; boost::filesystem::remove_all(temp_path); } } @@ -815,6 +833,19 @@ std::string Model::get_backup_path() return backup_path; } +void Model::remove_backup_path_if_exist() +{ + if (!backup_path.empty()) { + boost::filesystem::path temp_path(backup_path); + if (boost::filesystem::exists(temp_path)) + { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("model %1%, id %2% remove backup_path %3%")%this%this->id().id%backup_path; + boost::filesystem::remove_all(temp_path); + } + backup_path.clear(); + } +} + std::string Model::get_backup_path(const std::string &sub_path) { auto path = get_backup_path() + "/" + sub_path; @@ -837,9 +868,12 @@ void Model::set_backup_path(std::string const& path) backup_path.clear(); return; } - if (!backup_path.empty()) + if (!backup_path.empty()) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__<id().id%backup_path; Slic3r::remove_backup(*this, true); + } backup_path = path; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__<id().id%backup_path; } void Model::load_from(Model& model) @@ -850,8 +884,10 @@ void Model::load_from(Model& model) next_object_backup_id = model.next_object_backup_id; design_info = model.design_info; model_info = model.model_info; + profile_info = model.profile_info; model.design_info.reset(); model.model_info.reset(); + model.profile_info.reset(); } // BBS: backup @@ -880,6 +916,25 @@ bool Model::is_mm_painted() const return std::any_of(this->objects.cbegin(), this->objects.cend(), [](const ModelObject *mo) { return mo->is_mm_painted(); }); } + +static void add_cut_volume(TriangleMesh& mesh, ModelObject* object, const ModelVolume* src_volume, const Transform3d& cut_matrix, const std::string& suffix = {}, ModelVolumeType type = ModelVolumeType::MODEL_PART) +{ + if (mesh.empty()) + return; + + mesh.transform(cut_matrix); + ModelVolume* vol = object->add_volume(mesh); + vol->set_type(type); + + vol->name = src_volume->name + suffix; + // Don't copy the config's ID. + vol->config.assign_config(src_volume->config); + assert(vol->config.id().valid()); + assert(vol->config.id() != src_volume->config.id()); + vol->set_material(src_volume->material_id(), *src_volume->material()); + vol->cut_info = src_volume->cut_info; +} + ModelObject::~ModelObject() { this->clear_volumes(); @@ -907,6 +962,7 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs) this->layer_height_profile = rhs.layer_height_profile; this->printable = rhs.printable; this->origin_translation = rhs.origin_translation; + this->cut_id.copy(rhs.cut_id); m_bounding_box = rhs.m_bounding_box; m_bounding_box_valid = rhs.m_bounding_box_valid; m_raw_bounding_box = rhs.m_raw_bounding_box; @@ -1019,6 +1075,9 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other, ModelVolumeType t ModelVolume* v = new ModelVolume(this, other); if (type != ModelVolumeType::INVALID && v->type() != type) v->set_type(type); + + v->cut_info = other.cut_info; + this->volumes.push_back(v); // The volume should already be centered at this point of time when copying shared pointers of the triangle mesh and convex hull. // v->center_geometry_after_creation(); @@ -1571,6 +1630,375 @@ size_t ModelObject::parts_count() const return num; } +bool ModelObject::has_connectors() const +{ + assert(is_cut()); + for (const ModelVolume *v : this->volumes) + if (v->cut_info.is_connector) return true; + + return false; +} + +indexed_triangle_set ModelObject::get_connector_mesh(CutConnectorAttributes connector_attributes) +{ + indexed_triangle_set connector_mesh; + + int sectorCount {1}; + switch (CutConnectorShape(connector_attributes.shape)) { + case CutConnectorShape::Triangle: + sectorCount = 3; + break; + case CutConnectorShape::Square: + sectorCount = 4; + break; + case CutConnectorShape::Circle: + sectorCount = 360; + break; + case CutConnectorShape::Hexagon: + sectorCount = 6; + break; + default: + break; + } + + if (connector_attributes.style == CutConnectorStyle::Prizm) + connector_mesh = its_make_cylinder(1.0, 1.0, (2 * PI / sectorCount)); + else if (connector_attributes.type == CutConnectorType::Plug) + connector_mesh = its_make_cone(1.0, 1.0, (2 * PI / sectorCount)); + else + connector_mesh = its_make_frustum_dowel(1.0, 1.0, sectorCount); + + return connector_mesh; +} + +void ModelObject::apply_cut_connectors(const std::string &name) +{ + if (cut_connectors.empty()) + return; + + using namespace Geometry; + + size_t connector_id = cut_id.connectors_cnt(); + for (const CutConnector &connector : cut_connectors) { + TriangleMesh mesh = TriangleMesh(get_connector_mesh(connector.attribs)); + // Mesh will be centered when loading. + ModelVolume *new_volume = add_volume(std::move(mesh), ModelVolumeType::NEGATIVE_VOLUME); + + Transform3d translate_transform = Transform3d::Identity(); + translate_transform.translate(connector.pos); + Transform3d scale_transform = Transform3d::Identity(); + scale_transform.scale(Vec3f(connector.radius, connector.radius, connector.height).cast()); + + // Transform the new modifier to be aligned inside the instance + new_volume->set_transformation(translate_transform * connector.rotation_m * scale_transform); + + new_volume->cut_info = {connector.attribs.type, connector.radius_tolerance, connector.height_tolerance}; + new_volume->name = name + "-" + std::to_string(++connector_id); + } + cut_id.increase_connectors_cnt(cut_connectors.size()); + + // delete all connectors + cut_connectors.clear(); +} + +void ModelObject::invalidate_cut() +{ + this->cut_id.invalidate(); + for (ModelVolume *volume : this->volumes) + volume->invalidate_cut_info(); +} + +void ModelObject::delete_connectors() +{ + for (int id = int(this->volumes.size()) - 1; id >= 0; id--) { + if (volumes[id]->is_cut_connector()) + this->delete_volume(size_t(id)); + } +} + +void ModelObject::synchronize_model_after_cut() +{ + for (ModelObject *obj : m_model->objects) { + if (obj == this || obj->cut_id.is_equal(this->cut_id)) continue; + if (obj->is_cut() && obj->cut_id.has_same_id(this->cut_id)) + obj->cut_id.copy(this->cut_id); + } +} + +void ModelObject::apply_cut_attributes(ModelObjectCutAttributes attributes) +{ + // we don't save cut information, if result will not contains all parts of initial object + if (!attributes.has(ModelObjectCutAttribute::KeepUpper) || + !attributes.has(ModelObjectCutAttribute::KeepLower) || + attributes.has(ModelObjectCutAttribute::InvalidateCutInfo)) + return; + + if (cut_id.id().invalid()) + cut_id.init(); + + { + int cut_obj_cnt = -1; + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) + cut_obj_cnt++; + if (attributes.has(ModelObjectCutAttribute::KeepLower)) + cut_obj_cnt++; + if (attributes.has(ModelObjectCutAttribute::CreateDowels)) + cut_obj_cnt++; + if (cut_obj_cnt > 0) + cut_id.increase_check_sum(size_t(cut_obj_cnt)); + } +} + +void ModelObject::clone_for_cut(ModelObject **obj) +{ + (*obj) = ModelObject::new_clone(*this); + (*obj)->set_model(nullptr); + (*obj)->sla_support_points.clear(); + (*obj)->sla_drain_holes.clear(); + (*obj)->sla_points_status = sla::PointsStatus::NoPoints; + (*obj)->clear_volumes(); + (*obj)->input_file.clear(); +} + +Transform3d ModelObject::calculate_cut_plane_inverse_matrix(const std::array& plane_points) +{ + Vec3d mid_point = {0.0, 0.0, 0.0}; + for (auto pt : plane_points) + mid_point += pt; + mid_point /= (double) plane_points.size(); + + Vec3d movement = -mid_point; + + Vec3d v01 = plane_points[1] - plane_points[0]; + Vec3d v12 = plane_points[2] - plane_points[1]; + + Vec3d plane_normal = v01.cross(v12); + plane_normal.normalize(); + + Vec3d axis = {0.0, 0.0, 0.0}; + double phi = 0.0; + Matrix3d matrix; + matrix.setIdentity(); + Geometry::rotation_from_two_vectors(plane_normal, {0.0, 0.0, 1.0}, axis, phi, &matrix); + Vec3d angles = Geometry::extract_euler_angles(matrix); + + movement = matrix * movement; + Transform3d transfo; + transfo.setIdentity(); + transfo.translate(movement); + transfo.rotate(Eigen::AngleAxisd(angles(2), Vec3d::UnitZ()) * Eigen::AngleAxisd(angles(1), Vec3d::UnitY()) * Eigen::AngleAxisd(angles(0), Vec3d::UnitX())); + return transfo; +} + +void ModelObject::process_connector_cut( + ModelVolume *volume, + const Transform3d & instance_matrix, + const Transform3d& cut_matrix, + ModelObjectCutAttributes attributes, + ModelObject *upper, ModelObject *lower, + std::vector &dowels, + Vec3d &local_dowels_displace) +{ + assert(volume->cut_info.is_connector); + volume->cut_info.set_processed(); + + const auto volume_matrix = volume->get_matrix(); + + // ! Don't apply instance transformation for the conntectors. + // This transformation is already there + if (volume->cut_info.connector_type != CutConnectorType::Dowel) { + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) { + ModelVolume *vol = upper->add_volume(*volume); + vol->set_transformation(volume_matrix); + vol->apply_tolerance(); + } + if (attributes.has(ModelObjectCutAttribute::KeepLower)) { + ModelVolume *vol = lower->add_volume(*volume); + vol->set_transformation(volume_matrix); + // for lower part change type of connector from NEGATIVE_VOLUME to MODEL_PART if this connector is a plug + vol->set_type(ModelVolumeType::MODEL_PART); + } + } + else { + if (attributes.has(ModelObjectCutAttribute::CreateDowels)) { + ModelObject *dowel{nullptr}; + // Clone the object to duplicate instances, materials etc. + clone_for_cut(&dowel); + + // add one more solid part same as connector if this connector is a dowel + ModelVolume *vol = dowel->add_volume(*volume); + vol->set_type(ModelVolumeType::MODEL_PART); + + // But discard rotation and Z-offset for this volume + vol->set_rotation(Vec3d::Zero()); + vol->set_offset(Z, 0.0); + + // Compute the displacement (in instance coordinates) to be applied to place the dowels + local_dowels_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(1.0, 1.0, 0.0)); + + dowels.push_back(dowel); + } + + // Cut the dowel + volume->apply_tolerance(); + + // Perform cut + TriangleMesh upper_mesh, lower_mesh; + process_volume_cut(volume, instance_matrix, cut_matrix, attributes, upper_mesh, lower_mesh); + + // add small Z offset to better preview + upper_mesh.translate((-0.05 * Vec3d::UnitZ()).cast()); + lower_mesh.translate((0.05 * Vec3d::UnitZ()).cast()); + + // Add cut parts to the related objects + add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A", volume->type()); + add_cut_volume(lower_mesh, lower, volume, cut_matrix, "_B", volume->type()); + } +} + +void ModelObject::process_modifier_cut( + ModelVolume *volume, + const Transform3d &instance_matrix, + const Transform3d &inverse_cut_matrix, + ModelObjectCutAttributes attributes, + ModelObject *upper, + ModelObject *lower) +{ + const auto volume_matrix = instance_matrix * volume->get_matrix(); + + // Modifiers are not cut, but we still need to add the instance transformation + // to the modifier volume transformation to preserve their shape properly. + volume->set_transformation(Geometry::Transformation(volume_matrix)); + + if (attributes.has(ModelObjectCutAttribute::CutToParts)) { + upper->add_volume(*volume); + return; + } + + // Some logic for the negative volumes/connectors. Add only needed modifiers + auto bb = volume->mesh().transformed_bounding_box(inverse_cut_matrix * volume_matrix); + bool is_crossed_by_cut = bb.min[Z] <= 0 && bb.max[Z] >= 0; + if (attributes.has(ModelObjectCutAttribute::KeepUpper) && (bb.min[Z] >= 0 || is_crossed_by_cut)) + upper->add_volume(*volume); + if (attributes.has(ModelObjectCutAttribute::KeepLower) && (bb.max[Z] <= 0 || is_crossed_by_cut)) + lower->add_volume(*volume); +} + +void ModelObject::process_volume_cut(ModelVolume * volume, + const Transform3d & instance_matrix, + const Transform3d & cut_matrix, + ModelObjectCutAttributes attributes, + TriangleMesh & upper_mesh, + TriangleMesh & lower_mesh) +{ + const auto volume_matrix = volume->get_matrix(); + + using namespace Geometry; + + const Geometry::Transformation cut_transformation = Geometry::Transformation(cut_matrix); + const Transform3d invert_cut_matrix = cut_transformation.get_matrix(true, false, true, true).inverse() + * translation_transform(-1 * cut_transformation.get_offset()); + + // Transform the mesh by the combined transformation matrix. + // Flip the triangles in case the composite transformation is left handed. + TriangleMesh mesh(volume->mesh()); + mesh.transform(invert_cut_matrix * instance_matrix * volume_matrix, true); + + indexed_triangle_set upper_its, lower_its; + cut_mesh(mesh.its, 0.0f, &upper_its, &lower_its); + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) + upper_mesh = TriangleMesh(upper_its); + if (attributes.has(ModelObjectCutAttribute::KeepLower)) + lower_mesh = TriangleMesh(lower_its); +} + +void ModelObject::process_solid_part_cut(ModelVolume * volume, + const Transform3d & instance_matrix, + const Transform3d & cut_matrix, + const std::array &plane_points, + ModelObjectCutAttributes attributes, + ModelObject * upper, + ModelObject * lower, + Vec3d & local_displace) +{ + // Perform cut + TriangleMesh upper_mesh, lower_mesh; + process_volume_cut(volume, instance_matrix, cut_matrix, attributes, upper_mesh, lower_mesh); + + // Add required cut parts to the objects + if (attributes.has(ModelObjectCutAttribute::CutToParts)) { + add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A"); + add_cut_volume(lower_mesh, upper, volume, cut_matrix, "_B"); + return; + } + + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) + add_cut_volume(upper_mesh, upper, volume, cut_matrix); + + if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower_mesh.empty()) { + add_cut_volume(lower_mesh, lower, volume, cut_matrix); + + // Compute the displacement (in instance coordinates) to be applied to place the upper parts + // The upper part displacement is set to half of the lower part bounding box + // this is done in hope at least a part of the upper part will always be visible and draggable + local_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-0.5, -0.5, 0.0)); + } +} + +static void invalidate_translations(ModelObject* object, const ModelInstance* src_instance) +{ + if (!object->origin_translation.isApprox(Vec3d::Zero()) && src_instance->get_offset().isApprox(Vec3d::Zero())) { + object->center_around_origin(); + object->translate_instances(-object->origin_translation); + object->origin_translation = Vec3d::Zero(); + } + else { + object->invalidate_bounding_box(); + object->center_around_origin(); + } +} + +static void reset_instance_transformation(ModelObject* object, size_t src_instance_idx, const Transform3d& cut_matrix, + bool place_on_cut = false, bool flip = false, Vec3d local_displace = Vec3d::Zero()) +{ + using namespace Geometry; + + // Reset instance transformation except offset and Z-rotation + + for (size_t i = 0; i < object->instances.size(); ++i) { + auto& obj_instance = object->instances[i]; + const Vec3d offset = obj_instance->get_offset(); + const double rot_z = obj_instance->get_rotation().z(); + + obj_instance->set_transformation(Transformation()); + + const Vec3d displace = local_displace.isApprox(Vec3d::Zero()) ? Vec3d::Zero() : + rotation_transform(obj_instance->get_rotation()) * local_displace; + obj_instance->set_offset(offset + displace); + + Vec3d rotation = Vec3d::Zero(); + if (!flip && !place_on_cut) { + if ( i != src_instance_idx) + rotation[Z] = rot_z; + } + else { + Transform3d rotation_matrix = Transform3d::Identity(); + if (flip) + rotation_matrix = rotation_transform(PI * Vec3d::UnitX()); + + if (place_on_cut) + rotation_matrix = rotation_matrix * Transformation(cut_matrix).get_matrix(true, false, true, true).inverse(); + + if (i != src_instance_idx) + rotation_matrix = rotation_transform(rot_z * Vec3d::UnitZ()) * rotation_matrix; + + rotation = Transformation(rotation_matrix).get_rotation(); + } + + obj_instance->set_rotation(rotation); + } +} + // BBS: replace z with plane_points ModelObjectPtrs ModelObject::cut(size_t instance, std::array plane_points, ModelObjectCutAttributes attributes) { @@ -1579,30 +2007,16 @@ ModelObjectPtrs ModelObject::cut(size_t instance, std::array plane_poi BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - start"; - // Clone the object to duplicate instances, materials etc. - bool keep_upper = attributes.has(ModelObjectCutAttribute::KeepUpper); - bool keep_lower = attributes.has(ModelObjectCutAttribute::KeepLower); - bool cut_to_parts = attributes.has(ModelObjectCutAttribute::CutToParts); - ModelObject* upper = keep_upper ? ModelObject::new_clone(*this) : nullptr; - ModelObject* lower = (cut_to_parts&&upper!=nullptr) ? upper : (keep_lower ? ModelObject::new_clone(*this) : nullptr); + // apply cut attributes for object + apply_cut_attributes(attributes); - if (attributes.has(ModelObjectCutAttribute::KeepUpper)) { - upper->set_model(nullptr); - upper->sla_support_points.clear(); - upper->sla_drain_holes.clear(); - upper->sla_points_status = sla::PointsStatus::NoPoints; - upper->clear_volumes(); - upper->input_file.clear(); - } + ModelObject* upper{ nullptr }; + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) + clone_for_cut(&upper); - if (keep_lower && lower != upper) { - lower->set_model(nullptr); - lower->sla_support_points.clear(); - lower->sla_drain_holes.clear(); - lower->sla_points_status = sla::PointsStatus::NoPoints; - lower->clear_volumes(); - lower->input_file.clear(); - } + ModelObject* lower{ nullptr }; + if (attributes.has(ModelObjectCutAttribute::KeepLower) && !attributes.has(ModelObjectCutAttribute::CutToParts)) + clone_for_cut(&lower); // Because transformations are going to be applied to meshes directly, // we reset transformation of all instances and volumes, @@ -1622,10 +2036,14 @@ ModelObjectPtrs ModelObject::cut(size_t instance, std::array plane_poi for (Vec3d& point : plane_points) { point -= instances[instance]->get_offset(); } + Transform3d inverse_cut_matrix = calculate_cut_plane_inverse_matrix(plane_points); + Transform3d cut_matrix = inverse_cut_matrix.inverse(); + std::vector dowels; // Displacement (in instance coordinates) to be applied to place the upper parts Vec3d local_displace = Vec3d::Zero(); - + Vec3d local_dowels_displace = Vec3d::Zero(); + for (ModelVolume *volume : volumes) { const auto volume_matrix = volume->get_matrix(); @@ -1634,121 +2052,62 @@ ModelObjectPtrs ModelObject::cut(size_t instance, std::array plane_poi volume->mmu_segmentation_facets.reset(); if (! volume->is_model_part()) { - // Modifiers are not cut, but we still need to add the instance transformation - // to the modifier volume transformation to preserve their shape properly. - - volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix)); - - if (attributes.has(ModelObjectCutAttribute::KeepUpper)) - upper->add_volume(*volume); - if (attributes.has(ModelObjectCutAttribute::KeepLower)) - lower->add_volume(*volume); + if (volume->cut_info.is_processed) { + // Modifiers are not cut, but we still need to add the instance transformation + // to the modifier volume transformation to preserve their shape properly. + //Transform3d inverse_cut_matrix = calculate_cut_plane_inverse_matrix(plane_points); + process_modifier_cut(volume, instance_matrix, inverse_cut_matrix, attributes, upper, lower); + } + else { + process_connector_cut(volume, instance_matrix, cut_matrix, attributes, upper, lower, dowels, local_dowels_displace); + } } else if (! volume->mesh().empty()) { - // Transform the mesh by the combined transformation matrix. - // Flip the triangles in case the composite transformation is left handed. - TriangleMesh mesh(volume->mesh()); - mesh.transform(instance_matrix * volume_matrix, true); - volume->reset_mesh(); - // Reset volume transformation except for offset - const Vec3d offset = volume->get_offset(); - volume->set_transformation(Geometry::Transformation()); - volume->set_offset(offset); - - // Perform cut - TriangleMesh upper_mesh, lower_mesh; - { - indexed_triangle_set upper_its, lower_its; - cut_mesh(mesh.its, plane_points, &upper_its, &lower_its); - if (attributes.has(ModelObjectCutAttribute::KeepUpper)) - upper_mesh = TriangleMesh(upper_its); - if (attributes.has(ModelObjectCutAttribute::KeepLower)) - lower_mesh = TriangleMesh(lower_its); - } - - if (attributes.has(ModelObjectCutAttribute::KeepUpper) && ! upper_mesh.empty()) { - ModelVolume* vol = upper->add_volume(upper_mesh); - vol->name = volume->name.substr(0, volume->name.find_last_of('.')) + "_upper"; // BBS - // Don't copy the config's ID. - vol->config.assign_config(volume->config); - assert(vol->config.id().valid()); - assert(vol->config.id() != volume->config.id()); - vol->set_material(volume->material_id(), *volume->material()); - } - if (attributes.has(ModelObjectCutAttribute::KeepLower) && ! lower_mesh.empty()) { - ModelVolume* vol = lower->add_volume(lower_mesh); - vol->name = volume->name.substr(0, volume->name.find_last_of('.')) + "_lower"; // BBS - // Don't copy the config's ID. - vol->config.assign_config(volume->config); - assert(vol->config.id().valid()); - assert(vol->config.id() != volume->config.id()); - vol->set_material(volume->material_id(), *volume->material()); - - // Compute the displacement (in instance coordinates) to be applied to place the upper parts - // The upper part displacement is set to half of the lower part bounding box - // this is done in hope at least a part of the upper part will always be visible and draggable - local_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-0.5, -0.5, 0.0)); - } + process_solid_part_cut(volume, instance_matrix, cut_matrix, plane_points, attributes, upper, lower, local_displace); } } ModelObjectPtrs res; - - if (attributes.has(ModelObjectCutAttribute::KeepUpper) && upper->volumes.size() > 0) { - if (!upper->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) { - // BBS: do not move the parts if cut_to_parts - if (!cut_to_parts) { - upper->center_around_origin(); - upper->translate_instances(-upper->origin_translation); - upper->origin_translation = Vec3d::Zero(); - } - } - - // Reset instance transformation except offset and Z-rotation - for (size_t i = 0; i < instances.size(); ++i) { - auto &instance = upper->instances[i]; - const Vec3d offset = instance->get_offset(); - // BBS - //const double rot_z = instance->get_rotation().z(); - // BBS: do not move the parts if cut_to_parts - Vec3d displace(0, 0, 0); - if (!cut_to_parts) - displace = Geometry::assemble_transform(Vec3d::Zero(), instance->get_rotation()) * local_displace; - - instance->set_transformation(Geometry::Transformation()); - instance->set_offset(offset + displace); - // BBS - //instance->set_rotation(Vec3d(0.0, 0.0, rot_z)); - } - + + if (attributes.has(ModelObjectCutAttribute::CutToParts) && !upper->volumes.empty()) { + reset_instance_transformation(upper, instance, cut_matrix); res.push_back(upper); } - if (attributes.has(ModelObjectCutAttribute::KeepLower) && lower->volumes.size() > 0) { - if (!lower->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) { - if (!cut_to_parts) { - lower->center_around_origin(); - lower->translate_instances(-lower->origin_translation); - lower->origin_translation = Vec3d::Zero(); + else { + if (attributes.has(ModelObjectCutAttribute::KeepUpper) && upper->volumes.size() > 0) { + invalidate_translations(upper, instances[instance]); + + reset_instance_transformation(upper, instance, cut_matrix, attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper), + attributes.has(ModelObjectCutAttribute::FlipUpper), local_displace); + + res.push_back(upper); + } + if (attributes.has(ModelObjectCutAttribute::KeepLower) && lower->volumes.size() > 0) { + invalidate_translations(lower, instances[instance]); + + reset_instance_transformation(lower, instance, cut_matrix, attributes.has(ModelObjectCutAttribute::PlaceOnCutLower), + attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) ? true : attributes.has(ModelObjectCutAttribute::FlipLower)); + + res.push_back(lower); + } + + if (attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) { + for (auto dowel : dowels) { + invalidate_translations(dowel, instances[instance]); + + reset_instance_transformation(dowel, instance, Transform3d::Identity(), false, false, local_dowels_displace); + + local_dowels_displace += dowel->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-1.5, -1.5, 0.0)); + dowel->name += "-Dowel-" + dowel->volumes[0]->name; + res.push_back(dowel); } } - - // Reset instance transformation except offset and Z-rotation - for (auto *instance : lower->instances) { - const Vec3d offset = instance->get_offset(); - // BBS - //const double rot_z = instance->get_rotation().z(); - instance->set_transformation(Geometry::Transformation()); - instance->set_offset(offset); - // BBS - //instance->set_rotation(Vec3d(attributes.has(ModelObjectCutAttribute::FlipLower) ? Geometry::deg2rad(180.0) : 0.0, 0.0, rot_z)); - } - - if(res.empty() || lower != res.back()) - res.push_back(lower); } BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - end"; + synchronize_model_after_cut(); + return res; } @@ -2188,7 +2547,7 @@ unsigned int ModelObject::update_instances_print_volume_state(const BuildVolume // %print_volume.min.x() %print_volume.min.y() %print_volume.min.z()%print_volume.max.x() %print_volume.max.y() %print_volume.max.z(); for (ModelInstance* model_instance : this->instances) { unsigned int inside_outside = 0; - for (const ModelVolume* vol : this->volumes) + for (const ModelVolume *vol : this->volumes) { if (vol->is_model_part()) { //BBS: add bounding box empty check logic, for some volume is empty before split(it will be removed after split to object) BoundingBoxf3 bb = vol->get_convex_hull().bounding_box(); @@ -2214,6 +2573,7 @@ unsigned int ModelObject::update_instances_print_volume_state(const BuildVolume // Volume colliding with the build volume. inside_outside |= INSIDE | OUTSIDE; } + } model_instance->print_volume_state = inside_outside == (INSIDE | OUTSIDE) ? ModelInstancePVS_Partly_Outside : inside_outside == INSIDE ? ModelInstancePVS_Inside : ModelInstancePVS_Fully_Outside; @@ -2366,6 +2726,23 @@ bool ModelVolume::is_splittable() const return m_is_splittable == 1; } +void ModelVolume::apply_tolerance() +{ + assert(cut_info.is_connector); + if (!cut_info.is_processed) + return; + + Vec3d sf = get_scaling_factor(); + // make a "hole" wider + sf[X] *= 1. + double(cut_info.radius_tolerance); + sf[Y] *= 1. + double(cut_info.radius_tolerance); + + // make a "hole" dipper + sf[Z] *= 1. + double(cut_info.height_tolerance); + + set_scaling_factor(sf); +} + // BBS std::vector ModelVolume::get_extruders() const { @@ -2989,18 +3366,20 @@ void ModelInstance::get_arrange_polygon(void *ap, const Slic3r::DynamicPrintConf return; } ret.extrude_ids = volume->get_extruders(); - if (ret.extrude_ids.empty()) //the default extruder - ret.extrude_ids.push_back(1); - // get per-object support extruders auto op = object->get_config_value(config_global, "enable_support"); bool is_support_enabled = op && op->getBool(); if (is_support_enabled) { auto op1 = object->get_config_value(config_global, "support_filament"); auto op2 = object->get_config_value(config_global, "support_interface_filament"); - if (op1) ret.extrude_ids.push_back(op1->getInt()); - if (op2) ret.extrude_ids.push_back(op2->getInt()); + int extruder_id; + // id==0 means follow previous material, so need not be recorded + if (op1 && (extruder_id = op1->getInt()) > 0) ret.extrude_ids.push_back(extruder_id); + if (op2 && (extruder_id = op2->getInt()) > 0) ret.extrude_ids.push_back(extruder_id); } + + if (ret.extrude_ids.empty()) //the default extruder + ret.extrude_ids.push_back(1); } indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, EnforcerBlockerType type) const diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 551a9072ee..a3002b22df 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -232,6 +232,80 @@ private: friend class ModelObject; }; +enum class CutConnectorType : int { + Plug, + Dowel, + Undef +}; + +enum class CutConnectorStyle : int { + Prizm, + Frustum, + Undef + //,Claw +}; + +enum class CutConnectorShape : int { + Triangle, + Square, + Hexagon, + Circle, + Undef + //,D-shape +}; + +struct CutConnectorAttributes +{ + CutConnectorType type{CutConnectorType::Plug}; + CutConnectorStyle style{CutConnectorStyle::Prizm}; + CutConnectorShape shape{CutConnectorShape::Circle}; + + CutConnectorAttributes() {} + + CutConnectorAttributes(CutConnectorType t, CutConnectorStyle st, CutConnectorShape sh) : type(t), style(st), shape(sh) {} + + CutConnectorAttributes(const CutConnectorAttributes &rhs) : CutConnectorAttributes(rhs.type, rhs.style, rhs.shape) {} + + bool operator==(const CutConnectorAttributes &other) const; + + bool operator!=(const CutConnectorAttributes &other) const { return !(other == (*this)); } + + bool operator<(const CutConnectorAttributes &other) const + { + return this->type < other.type || (this->type == other.type && this->style < other.style) || + (this->type == other.type && this->style == other.style && this->shape < other.shape); + } + + template inline void serialize(Archive &ar) { ar(type, style, shape); } +}; + +struct CutConnector +{ + Vec3d pos; + Transform3d rotation_m; + float radius; + float height; + float radius_tolerance; // [0.f : 1.f] + float height_tolerance; // [0.f : 1.f] + CutConnectorAttributes attribs; + + CutConnector() : pos(Vec3d::Zero()), rotation_m(Transform3d::Identity()), radius(5.f), height(10.f), radius_tolerance(0.f), height_tolerance(0.1f) {} + + CutConnector(Vec3d p, Transform3d rot, float r, float h, float rt, float ht, CutConnectorAttributes attributes) + : pos(p), rotation_m(rot), radius(r), height(h), radius_tolerance(rt), height_tolerance(ht), attribs(attributes) + {} + + CutConnector(const CutConnector &rhs) : CutConnector(rhs.pos, rhs.rotation_m, rhs.radius, rhs.height, rhs.radius_tolerance, rhs.height_tolerance, rhs.attribs) {} + + bool operator==(const CutConnector &other) const; + + bool operator!=(const CutConnector &other) const { return !(other == (*this)); } + + template inline void serialize(Archive &ar) { ar(pos, rotation_m, radius, height, radius_tolerance, height_tolerance, attribs); } +}; + +using CutConnectors = std::vector; + // Declared outside of ModelVolume, so it could be forward declared. enum class ModelVolumeType : int { INVALID = -1, @@ -242,7 +316,7 @@ enum class ModelVolumeType : int { SUPPORT_ENFORCER }; -enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, FlipLower, CutToParts }; +enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, FlipUpper, FlipLower, PlaceOnCutUpper, PlaceOnCutLower, CreateDowels, CutToParts, InvalidateCutInfo }; using ModelObjectCutAttributes = enum_bitmask; ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute); @@ -293,6 +367,10 @@ public: // BBS: save for compare with new load volumes std::vector volume_ids; + // Connectors to be added into the object before cut and are used to create a solid/negative volumes during a cut perform + CutConnectors cut_connectors; + CutObjectBase cut_id; + Model* get_model() { return m_model; } const Model* get_model() const { return m_model; } // BBS: production extension @@ -385,6 +463,47 @@ public: size_t materials_count() const; size_t facets_count() const; size_t parts_count() const; + + bool is_cut() const { return cut_id.id().valid(); } + bool has_connectors() const; + static indexed_triangle_set get_connector_mesh(CutConnectorAttributes connector_attributes); + void apply_cut_connectors(const std::string &name); + // invalidate cut state for this object and its connectors/volumes + void invalidate_cut(); + // delete volumes which are marked as connector for this object + void delete_connectors(); + void synchronize_model_after_cut(); + void apply_cut_attributes(ModelObjectCutAttributes attributes); + void clone_for_cut(ModelObject **obj); + Transform3d calculate_cut_plane_inverse_matrix(const std::array &plane_points); + void process_connector_cut(ModelVolume *volume, + const Transform3d & instance_matrix, + const Transform3d& cut_matrix, + ModelObjectCutAttributes attributes, + ModelObject *upper, ModelObject *lower, + std::vector &dowels, + Vec3d &local_dowels_displace); + void process_modifier_cut(ModelVolume * volume, + const Transform3d & instance_matrix, + const Transform3d & inverse_cut_matrix, + ModelObjectCutAttributes attributes, + ModelObject * upper, + ModelObject * lower); + void process_volume_cut(ModelVolume * volume, + const Transform3d & instance_matrix, + const Transform3d & cut_matrix, + ModelObjectCutAttributes attributes, + TriangleMesh & upper_mesh, + TriangleMesh & lower_mesh); + void process_solid_part_cut(ModelVolume * volume, + const Transform3d & instance_matrix, + const Transform3d & cut_matrix, + const std::array &plane_points, + ModelObjectCutAttributes attributes, + ModelObject * upper, + ModelObject * lower, + Vec3d & local_displace); + // BBS: replace z with plane_points ModelObjectPtrs cut(size_t instance, std::array plane_points, ModelObjectCutAttributes attributes); // BBS @@ -533,7 +652,8 @@ private: Internal::StaticSerializationWrapper layer_heigth_profile_wrapper(layer_height_profile); ar(name, module_name, input_file, instances, volumes, config_wrapper, layer_config_ranges, layer_heigth_profile_wrapper, sla_support_points, sla_points_status, sla_drain_holes, printable, origin_translation, - m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid); + m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid, + cut_connectors, cut_id); } template void load(Archive& ar) { ar(cereal::base_class(this)); @@ -543,7 +663,8 @@ private: SaveObjectGaurd gaurd(*this); ar(name, module_name, input_file, instances, volumes, config_wrapper, layer_config_ranges, layer_heigth_profile_wrapper, sla_support_points, sla_points_status, sla_drain_holes, printable, origin_translation, - m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid); + m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid, + cut_connectors, cut_id); std::vector volume_ids2; std::transform(volumes.begin(), volumes.end(), std::back_inserter(volume_ids2), std::mem_fn(&ObjectBase::id)); if (volume_ids != volume_ids2) @@ -710,6 +831,31 @@ public: }; Source source; + // struct used by cut command + // It contains information about connetors + struct CutInfo + { + bool is_connector{false}; + bool is_processed{true}; + CutConnectorType connector_type{CutConnectorType::Plug}; + float radius_tolerance{0.f}; // [0.f : 1.f] + float height_tolerance{0.f}; // [0.f : 1.f] + + CutInfo() = default; + CutInfo(CutConnectorType type, float rad_tolerance, float h_tolerance, bool processed = false) + : is_connector(true), is_processed(processed), connector_type(type), radius_tolerance(rad_tolerance), height_tolerance(h_tolerance) + {} + + void set_processed() { is_processed = true; } + void invalidate() { is_connector = false; } + + template inline void serialize(Archive &ar) { ar(is_connector, is_processed, connector_type, radius_tolerance, height_tolerance); } + }; + CutInfo cut_info; + + bool is_cut_connector() const { return cut_info.is_processed && cut_info.is_connector; } + void invalidate_cut_info() { cut_info.invalidate(); } + // The triangular model. const TriangleMesh& mesh() const { return *m_mesh.get(); } const TriangleMesh* mesh_ptr() const { return m_mesh.get(); } @@ -760,6 +906,8 @@ public: bool is_splittable() const; + void apply_tolerance(); + // BBS std::vector get_extruders() const; void update_extruder_count(size_t extruder_count); @@ -1001,7 +1149,7 @@ private: // BBS: add backup, check modify bool mesh_changed = false; auto tr = m_transformation; - ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull, m_text_info); + ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull, m_text_info, cut_info); mesh_changed |= !(tr == m_transformation); if (mesh_changed) m_transformation.get_matrix(true, true, true, true); // force dirty auto t = supported_facets.timestamp(); @@ -1027,7 +1175,7 @@ private: } template void save(Archive &ar) const { bool has_convex_hull = m_convex_hull.get() != nullptr; - ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull, m_text_info); + ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull, m_text_info, cut_info); cereal::save_by_value(ar, supported_facets); cereal::save_by_value(ar, seam_facets); cereal::save_by_value(ar, mmu_segmentation_facets); @@ -1072,6 +1220,7 @@ public: // Whether or not this instance is printable bool printable; int arrange_order = 0; // BBS + size_t loaded_id = 0; // BBS ModelObject* get_object() const { return this->object; } @@ -1260,6 +1409,17 @@ struct GlobalSpeedMap Polygon bed_poly; }; +/* Profile data */ +class ModelProfileInfo +{ +public: + std::string ProfileTile; + std::string ProfileCover; + std::string ProfileDescription; + std::string ProfileUserId; + std::string ProfileUserName; +}; + /* info in ModelDesignInfo can not changed after initialization */ class ModelDesignInfo { @@ -1278,6 +1438,7 @@ public: std::string description; // utf8 format std::string copyright; // utf8 format std::string model_name; // utf8 format + std::string origin; // utf8 format std::map metadata_items; // other meta data items @@ -1287,6 +1448,7 @@ public: this->description = info.description; this->copyright = info.copyright; this->model_name = info.model_name; + this->origin = info.origin; this->metadata_items = info.metadata_items; } }; @@ -1313,6 +1475,7 @@ public: // DesignInfo of Model std::shared_ptr design_info = nullptr; std::shared_ptr model_info = nullptr; + std::shared_ptr profile_info = nullptr; void SetDesigner(std::string designer, std::string designer_user_id) { if (design_info == nullptr) { @@ -1436,6 +1599,7 @@ public: void load_from(Model & model); bool is_need_backup() { return need_backup; } void set_need_backup(); + void remove_backup_path_if_exist(); // Checks if any of objects is painted using the fdm support painting gizmo. bool is_fdm_support_painted() const; diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp index 03f3268210..06efb5fdd5 100644 --- a/src/libslic3r/MultiMaterialSegmentation.cpp +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -48,6 +48,13 @@ struct segment_traits { //#define MMU_SEGMENTATION_DEBUG_COLORIZED_POLYGONS namespace Slic3r { +bool is_equal(float left, float right, float eps = 1e-3) { + return abs(left - right) <= eps; +} + +bool is_less(float left, float right, float eps = 1e-3) { + return left + eps < right; +} // Assumes that is at most same projected_l length or below than projection_l static bool project_line_on_line(const Line &projection_l, const Line &projected_l, Line *new_projected) @@ -1518,6 +1525,12 @@ static inline std::vector> mmu_segmentation_top_and_bott triangles_by_color_bottom.assign(num_extruders, std::vector(num_layers * 2)); triangles_by_color_top.assign(num_extruders, std::vector(num_layers * 2)); + // BBS: use shell_triangles_by_color_bottom & shell_triangles_by_color_top to save the top and bottom embedded layers's color information + std::vector> shell_triangles_by_color_bottom(num_extruders); + std::vector> shell_triangles_by_color_top(num_extruders); + shell_triangles_by_color_bottom.assign(num_extruders, std::vector(num_layers * 2)); + shell_triangles_by_color_top.assign(num_extruders, std::vector(num_layers * 2)); + struct LayerColorStat { // Number of regions for a queried color. int num_regions { 0 }; @@ -1560,7 +1573,8 @@ static inline std::vector> mmu_segmentation_top_and_bott }; tbb::parallel_for(tbb::blocked_range(0, num_layers, granularity), [&granularity, &num_layers, &num_extruders, &layer_color_stat, &top_raw, &triangles_by_color_top, - &throw_on_cancel_callback, &input_expolygons, &bottom_raw, &triangles_by_color_bottom](const tbb::blocked_range &range) { + &throw_on_cancel_callback, &input_expolygons, &bottom_raw, &triangles_by_color_bottom, + &shell_triangles_by_color_top, &shell_triangles_by_color_bottom](const tbb::blocked_range &range) { size_t group_idx = range.begin() / granularity; size_t layer_idx_offset = (group_idx & 1) * num_layers; for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { @@ -1575,7 +1589,7 @@ static inline std::vector> mmu_segmentation_top_and_bott append(triangles_by_color_top[color_idx][layer_idx + layer_idx_offset], top_ex); float offset = 0.f; ExPolygons layer_slices_trimmed = input_expolygons[layer_idx]; - for (int last_idx = int(layer_idx) - 1; last_idx >= std::max(int(layer_idx - stat.top_shell_layers), int(0)); --last_idx) { + for (int last_idx = int(layer_idx) - 1; last_idx > std::max(int(layer_idx - stat.top_shell_layers), int(0)); --last_idx) { //BBS: offset width should be 2*spacing to avoid too narrow area which has overlap of wall line //offset -= stat.extrusion_width ; offset -= (stat.extrusion_spacing + stat.extrusion_width); @@ -1583,7 +1597,7 @@ static inline std::vector> mmu_segmentation_top_and_bott ExPolygons last = opening_ex(intersection_ex(top_ex, offset_ex(layer_slices_trimmed, offset)), stat.small_region_threshold); if (last.empty()) break; - append(triangles_by_color_top[color_idx][last_idx + layer_idx_offset], std::move(last)); + append(shell_triangles_by_color_top[color_idx][last_idx + layer_idx_offset], std::move(last)); } } } @@ -1603,7 +1617,7 @@ static inline std::vector> mmu_segmentation_top_and_bott ExPolygons last = opening_ex(intersection_ex(bottom_ex, offset_ex(layer_slices_trimmed, offset)), stat.small_region_threshold); if (last.empty()) break; - append(triangles_by_color_bottom[color_idx][last_idx + layer_idx_offset], std::move(last)); + append(shell_triangles_by_color_bottom[color_idx][last_idx + layer_idx_offset], std::move(last)); } } } @@ -1613,9 +1627,11 @@ static inline std::vector> mmu_segmentation_top_and_bott std::vector> triangles_by_color_merged(num_extruders); triangles_by_color_merged.assign(num_extruders, std::vector(num_layers)); - tbb::parallel_for(tbb::blocked_range(0, num_layers), [&triangles_by_color_merged, &triangles_by_color_bottom, &triangles_by_color_top, &num_layers, &throw_on_cancel_callback](const tbb::blocked_range &range) { + tbb::parallel_for(tbb::blocked_range(0, num_layers), [&triangles_by_color_merged, &triangles_by_color_bottom, &triangles_by_color_top, &num_layers, &throw_on_cancel_callback, + &shell_triangles_by_color_top, &shell_triangles_by_color_bottom](const tbb::blocked_range &range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { throw_on_cancel_callback(); + ExPolygons painted_exploys; for (size_t color_idx = 0; color_idx < triangles_by_color_merged.size(); ++color_idx) { auto &self = triangles_by_color_merged[color_idx][layer_idx]; append(self, std::move(triangles_by_color_bottom[color_idx][layer_idx])); @@ -1623,6 +1639,27 @@ static inline std::vector> mmu_segmentation_top_and_bott append(self, std::move(triangles_by_color_top[color_idx][layer_idx])); append(self, std::move(triangles_by_color_top[color_idx][layer_idx + num_layers])); self = union_ex(self); + + append(painted_exploys, self); + } + + painted_exploys = union_ex(painted_exploys); + + //BBS: merge the top and bottom shell layers + for (size_t color_idx = 0; color_idx < triangles_by_color_merged.size(); ++color_idx) { + auto &self = triangles_by_color_merged[color_idx][layer_idx]; + + auto top_area = diff_ex(union_ex(shell_triangles_by_color_top[color_idx][layer_idx], + shell_triangles_by_color_top[color_idx][layer_idx + num_layers]), + painted_exploys); + + auto bottom_area = diff_ex(union_ex(shell_triangles_by_color_bottom[color_idx][layer_idx], + shell_triangles_by_color_bottom[color_idx][layer_idx + num_layers]), + painted_exploys); + + append(self, top_area); + append(self, bottom_area); + self = union_ex(self); } // Trim one region by the other if some of the regions overlap. for (size_t color_idx = 1; color_idx < triangles_by_color_merged.size(); ++ color_idx) @@ -1883,7 +1920,7 @@ std::vector> multi_material_segmentation_by_painting(con for (auto layer_it = first_layer; layer_it != (last_layer + 1); ++layer_it) { const Layer *layer = *layer_it; size_t layer_idx = layer_it - layers.begin(); - if (input_expolygons[layer_idx].empty() || facet[0].z() > layer->slice_z || layer->slice_z > facet[2].z()) + if (input_expolygons[layer_idx].empty() || is_less(layer->slice_z, facet[0].z()) || is_less(facet[2].z(), layer->slice_z)) continue; // https://kandepet.com/3d-printing-slicing-3d-objects/ @@ -1891,7 +1928,12 @@ std::vector> multi_material_segmentation_by_painting(con Vec3f line_start_f = facet[0] + t * (facet[2] - facet[0]); Vec3f line_end_f; - if (facet[1].z() > layer->slice_z) { + // BBS: When one side of a triangle coincides with the slice_z. + if ((is_equal(facet[0].z(), facet[1].z()) && is_equal(facet[1].z(), layer->slice_z)) + || (is_equal(facet[1].z(), facet[2].z()) && is_equal(facet[1].z(), layer->slice_z))) { + line_end_f = facet[1]; + } + else if (facet[1].z() > layer->slice_z) { // [P0, P2] and [P0, P1] float t1 = (float(layer->slice_z) - facet[0].z()) / (facet[1].z() - facet[0].z()); line_end_f = facet[0] + t1 * (facet[1] - facet[0]); diff --git a/src/libslic3r/ObjectID.hpp b/src/libslic3r/ObjectID.hpp index 1030171e7f..b4e0507f41 100644 --- a/src/libslic3r/ObjectID.hpp +++ b/src/libslic3r/ObjectID.hpp @@ -65,6 +65,8 @@ protected: // Constructor with ignored int parameter to assign an invalid ID, to be replaced // by an existing ID copied from elsewhere. ObjectBase(int) : m_id(ObjectID(0)) {} + + ObjectBase(const ObjectID id) : m_id(id) {} // The class tree will have virtual tables and type information. virtual ~ObjectBase() = default; @@ -89,7 +91,6 @@ private: friend class cereal::access; friend class Slic3r::UndoRedo::StackImpl; template void serialize(Archive &ar) { ar(m_id); } - ObjectBase(const ObjectID id) : m_id(id) {} template static void load_and_construct(Archive & ar, cereal::construct &construct) { ObjectID id; ar(id); construct(id); } }; @@ -128,6 +129,67 @@ private: template void serialize(Archive &ar) { ar(m_timestamp); } }; +class CutObjectBase : public ObjectBase +{ + // check sum of CutParts in initial Object + size_t m_check_sum{1}; + // connectors count + size_t m_connectors_cnt{0}; + +public: + // Default Constructor to assign an invalid ID + CutObjectBase() : ObjectBase(-1) {} + // Constructor with ignored int parameter to assign an invalid ID, to be replaced + // by an existing ID copied from elsewhere. + CutObjectBase(int) : ObjectBase(-1) {} + // Constructor to initialize full information from 3mf + CutObjectBase(ObjectID id, size_t check_sum, size_t connectors_cnt) : ObjectBase(id), m_check_sum(check_sum), m_connectors_cnt(connectors_cnt) {} + // The class tree will have virtual tables and type information. + virtual ~CutObjectBase() = default; + + bool operator<(const CutObjectBase &other) const { return other.id() > this->id(); } + bool operator==(const CutObjectBase &other) const { return other.id() == this->id(); } + + void copy(const CutObjectBase &rhs) + { + this->copy_id(rhs); + this->m_check_sum = rhs.check_sum(); + this->m_connectors_cnt = rhs.connectors_cnt(); + } + CutObjectBase &operator=(const CutObjectBase &other) + { + this->copy(other); + return *this; + } + + void invalidate() + { + set_invalid_id(); + m_check_sum = 1; + m_connectors_cnt = 0; + } + + void init() { this->set_new_unique_id(); } + bool has_same_id(const CutObjectBase &rhs) { return this->id() == rhs.id(); } + bool is_equal(const CutObjectBase &rhs) { return this->id() == rhs.id() && this->check_sum() == rhs.check_sum() && this->connectors_cnt() == rhs.connectors_cnt(); } + + size_t check_sum() const { return m_check_sum; } + void set_check_sum(size_t cs) { m_check_sum = cs; } + void increase_check_sum(size_t cnt) { m_check_sum += cnt; } + + size_t connectors_cnt() const { return m_connectors_cnt; } + void increase_connectors_cnt(size_t connectors_cnt) { m_connectors_cnt += connectors_cnt; } + +private: + friend class cereal::access; + template void serialize(Archive &ar) + { + ar(cereal::base_class(this)); + ar(m_check_sum, m_connectors_cnt); + } +}; + + // Unique object / instance ID for the wipe tower. extern ObjectID wipe_tower_object_id(); extern ObjectID wipe_tower_instance_id(); diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 4925e7bc45..cb75a2cc40 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -268,6 +268,10 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime } if (perimeter_generator.config->detect_overhang_wall && perimeter_generator.layer_id > perimeter_generator.object_config->raft_layers) { // get non 100% overhang paths by intersecting this loop with the grown lower slices + // prepare grown lower layer slices for overhang detection + BoundingBox bbox(polygon.points); + bbox.offset(SCALED_EPSILON); + Polylines remain_polines; //BBS: don't calculate overhang degree when enable fuzzy skin. It's unmeaning @@ -275,10 +279,10 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime for (auto it = lower_polygons_series->begin(); it != lower_polygons_series->end(); it++) { + Polygons lower_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(it->second, bbox); - Polylines inside_polines = (it == lower_polygons_series->begin()) ? - intersection_pl({ polygon }, it->second) : - intersection_pl(remain_polines, it->second); + Polylines inside_polines = (it == lower_polygons_series->begin()) ? intersection_pl({polygon}, lower_polygons_series_clipped) : + intersection_pl(remain_polines, lower_polygons_series_clipped); extrusion_paths_append( paths, std::move(inside_polines), @@ -289,9 +293,8 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime extrusion_width, (float)perimeter_generator.layer_height); - remain_polines = (it == lower_polygons_series->begin()) ? - diff_pl({ polygon }, it->second) : - diff_pl(remain_polines, it->second); + remain_polines = (it == lower_polygons_series->begin()) ? diff_pl({polygon}, lower_polygons_series_clipped) : + diff_pl(remain_polines, lower_polygons_series_clipped); if (remain_polines.size() == 0) break; @@ -299,7 +302,9 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime } else { auto it = lower_polygons_series->end(); it--; - Polylines inside_polines = intersection_pl({ polygon }, it->second); + Polygons lower_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(it->second, bbox); + + Polylines inside_polines = intersection_pl({polygon}, lower_polygons_series_clipped); extrusion_paths_append( paths, std::move(inside_polines), @@ -310,7 +315,7 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime extrusion_width, (float)perimeter_generator.layer_height); - remain_polines = diff_pl({ polygon }, it->second); + remain_polines = diff_pl({polygon}, lower_polygons_series_clipped); } // get 100% overhang paths by checking what parts of this loop fall @@ -490,6 +495,90 @@ struct PerimeterGeneratorArachneExtrusion bool fuzzify = false; }; + +static void smooth_overhang_level(ExtrusionPaths &paths) +{ + const double threshold_length = scale_(0.8); + const double filter_range = scale_(6.5); + + // 0.save old overhang series first which is input of filter + const int path_num = paths.size(); + if (path_num < 2) + // don't need to do filting if only has one path in vector + return; + std::vector old_overhang_series; + old_overhang_series.reserve(path_num); + for (int i = 0; i < path_num; i++) old_overhang_series.push_back(paths[i].get_overhang_degree()); + + for (int i = 0; i < path_num;) { + if ((paths[i].role() != erPerimeter && paths[i].role() != erExternalPerimeter)) { + i++; + continue; + } + + double current_length = paths[i].length(); + int current_overhang_degree = old_overhang_series[i]; + double total_lens = current_length; + int pt = i + 1; + + for (; pt < path_num; pt++) { + if (paths[pt].get_overhang_degree() != current_overhang_degree || (paths[pt].role() != erPerimeter && paths[pt].role() != erExternalPerimeter)) { + break; + } + total_lens += paths[pt].length(); + } + + if (total_lens < threshold_length) { + double left_total_length = (filter_range - total_lens) / 2; + double right_total_length = left_total_length; + + double temp_length; + int j = i - 1; + int index; + std::vector> neighbor_path; + while (left_total_length > 0) { + index = (j < 0) ? path_num - 1 : j; + if (paths[index].role() == erOverhangPerimeter) break; + temp_length = paths[index].length(); + if (temp_length > left_total_length) + neighbor_path.emplace_back(std::pair(left_total_length, old_overhang_series[index])); + else + neighbor_path.emplace_back(std::pair(temp_length, old_overhang_series[index])); + left_total_length -= temp_length; + j = index; + j--; + } + + j = pt; + while (right_total_length > 0) { + index = j % path_num; + if (paths[index].role() == erOverhangPerimeter) break; + temp_length = paths[index].length(); + if (temp_length > right_total_length) + neighbor_path.emplace_back(std::pair(right_total_length, old_overhang_series[index])); + else + neighbor_path.emplace_back(std::pair(temp_length, old_overhang_series[index])); + right_total_length -= temp_length; + j++; + } + + double sum = 0; + double length_sum = 0; + for (auto it = neighbor_path.begin(); it != neighbor_path.end(); it++) { + sum += (it->first * it->second); + length_sum += it->first; + } + + double average_overhang = (double) (total_lens * current_overhang_degree + sum) / (length_sum + total_lens); + + for (int idx=i; idx& pg_extrusions) { ExtrusionEntityCollection extrusion_coll; @@ -509,26 +598,87 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p if (perimeter_generator.config->detect_overhang_wall && perimeter_generator.layer_id > perimeter_generator.object_config->raft_layers && !((perimeter_generator.object_config->enable_support || perimeter_generator.object_config->enforce_support_layers > 0) && perimeter_generator.object_config->support_top_z_distance.value == 0)) { - ClipperLib_Z::Path extrusion_path; extrusion_path.reserve(extrusion->size()); - for (const Arachne::ExtrusionJunction& ej : extrusion->junctions) + BoundingBox extrusion_path_bbox; + for (const Arachne::ExtrusionJunction &ej : extrusion->junctions) { extrusion_path.emplace_back(ej.p.x(), ej.p.y(), ej.w); - - ClipperLib_Z::Paths lower_slices_paths; - lower_slices_paths.reserve(perimeter_generator.lower_slices_polygons().size()); - for (const Polygon& poly : perimeter_generator.lower_slices_polygons()) { - lower_slices_paths.emplace_back(); - ClipperLib_Z::Path& out = lower_slices_paths.back(); - out.reserve(poly.points.size()); - for (const Point& pt : poly.points) - out.emplace_back(pt.x(), pt.y(), 0); + extrusion_path_bbox.merge(Point(ej.p.x(), ej.p.y())); } - // get non-overhang paths by intersecting this loop with the grown lower slices - extrusion_paths_append(paths, clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctIntersection), role, - is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow); + ClipperLib_Z::Paths lower_slices_paths; + { + lower_slices_paths.reserve(perimeter_generator.lower_slices_polygons().size()); + Points clipped; + extrusion_path_bbox.offset(SCALED_EPSILON); + for (const Polygon &poly : perimeter_generator.lower_slices_polygons()) { + clipped.clear(); + ClipperUtils::clip_clipper_polygon_with_subject_bbox(poly.points, extrusion_path_bbox, clipped); + if (!clipped.empty()) { + lower_slices_paths.emplace_back(); + ClipperLib_Z::Path &out = lower_slices_paths.back(); + out.reserve(clipped.size()); + for (const Point &pt : clipped) + out.emplace_back(pt.x(), pt.y(), 0); + } + } + } + ExtrusionPaths temp_paths; + // get non-overhang paths by intersecting this loop with the grown lower slices + extrusion_paths_append(temp_paths, clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctIntersection), role, + is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow); + + if (perimeter_generator.config->enable_overhang_speed && perimeter_generator.config->fuzzy_skin == FuzzySkinType::None) { + + Flow flow = is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow; + std::map> clipper_serise; + + std::map recognization_paths; + for (const ExtrusionPath &path : temp_paths) { + if (recognization_paths.count(path.width)) + recognization_paths[path.width].emplace_back(std::move(path)); + else + recognization_paths.insert(std::pair(path.width, {std::move(path)})); + } + + for (const auto &it : recognization_paths) { + Polylines be_clipped; + + for (const ExtrusionPath &p : it.second) { + be_clipped.emplace_back(std::move(p.polyline)); + } + + BoundingBox extrusion_bboxs = get_extents(be_clipped); + //ExPolygons lower_slcier_chopped = *perimeter_generator.lower_slices; + Polygons lower_slcier_chopped=ClipperUtils::clip_clipper_polygons_with_subject_bbox(*perimeter_generator.lower_slices, extrusion_bboxs, true); + + double start_pos = -it.first * 0.5; + double end_pos = 0.5 * it.first; + + Polylines remain_polylines; + std::vector degree_polygons; + for (int j = 0; j < overhang_sampling_number; j++) { + Polygons limiton_polygons = offset(lower_slcier_chopped, float(scale_(start_pos + (j + 0.5) * (end_pos - start_pos) / (overhang_sampling_number - 1)))); + + Polylines inside_polines = j == 0 ? intersection_pl(be_clipped, limiton_polygons) : intersection_pl(remain_polylines, limiton_polygons); + + remain_polylines = j == 0 ? diff_pl(be_clipped, limiton_polygons) : diff_pl(remain_polylines, limiton_polygons); + + extrusion_paths_append(paths, std::move(inside_polines), j, int(0), role, it.second.front().mm3_per_mm, it.second.front().width, it.second.front().height); + + if (remain_polylines.size() == 0) break; + } + + if (remain_polylines.size() != 0) { + extrusion_paths_append(paths, std::move(remain_polylines), overhang_sampling_number - 1, int(0), erOverhangPerimeter, it.second.front().mm3_per_mm, it.second.front().width, it.second.front().height); + } + } + + } else { + paths = std::move(temp_paths); + + } // get overhang paths by checking what parts of this loop fall // outside the grown lower slices (thus where the distance between // the loop centerline and original lower slices is >= half nozzle diameter @@ -571,6 +721,12 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p } chain_and_reorder_extrusion_paths(paths, &start_point); + + if (perimeter_generator.config->enable_overhang_speed && perimeter_generator.config->fuzzy_skin == FuzzySkinType::None) { + // BBS: filter the speed + smooth_overhang_level(paths); + } + } } else { @@ -683,7 +839,7 @@ void PerimeterGenerator::process_classic() // extra perimeters for each one // BBS: don't simplify too much which influence arc fitting when export gcode if arc_fitting is enabled - double surface_simplify_resolution = (print_config->enable_arc_fitting) ? 0.1 * m_scaled_resolution : m_scaled_resolution; + double surface_simplify_resolution = (print_config->enable_arc_fitting && this->config->fuzzy_skin == FuzzySkinType::None) ? 0.2 * m_scaled_resolution : m_scaled_resolution; for (const Surface &surface : this->slices->surfaces) { // detect how many perimeters must be generated for this island int loop_number = this->config->wall_loops + surface.extra_perimeters - 1; // 0-indexed loops @@ -724,7 +880,7 @@ void PerimeterGenerator::process_classic() float(min_width / 2.)); // the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop for (ExPolygon &ex : expp) - ex.medial_axis(ext_perimeter_width + ext_perimeter_spacing2, min_width, &thin_walls); + ex.medial_axis(min_width, ext_perimeter_width + ext_perimeter_spacing2, &thin_walls); } else { coord_t ext_perimeter_smaller_width = this->smaller_ext_perimeter_flow.scaled_width(); for (const ExPolygon& expolygon : last) { @@ -803,8 +959,8 @@ void PerimeterGenerator::process_classic() break; } { - const bool fuzzify_contours = this->config->fuzzy_skin != FuzzySkinType::None && i == 0 && this->layer_id > 0; - const bool fuzzify_holes = fuzzify_contours && this->config->fuzzy_skin == FuzzySkinType::All; + const bool fuzzify_contours = this->config->fuzzy_skin != FuzzySkinType::None && ((i == 0 && this->layer_id > 0) || this->config->fuzzy_skin == FuzzySkinType::AllWalls); + const bool fuzzify_holes = fuzzify_contours && (this->config->fuzzy_skin == FuzzySkinType::All || this->config->fuzzy_skin == FuzzySkinType::AllWalls); for (const ExPolygon& expolygon : offsets) { // Outer contour may overlap with an inner contour, // inner contour may overlap with another inner contour, @@ -848,7 +1004,16 @@ void PerimeterGenerator::process_classic() offset_top_surface = 0; //don't takes into account too thin areas double min_width_top_surface = std::max(double(ext_perimeter_spacing / 2 + 10), 1.0 * (double(perimeter_width))); - ExPolygons grown_upper_slices = offset_ex(*this->upper_slices, min_width_top_surface); + + Polygons grown_upper_slices = offset(*this->upper_slices, min_width_top_surface); + + //BBS: get boungding box of last + BoundingBox last_box = get_extents(last); + last_box.offset(SCALED_EPSILON); + + // BBS: get the Polygons upper the polygon this layer + Polygons upper_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(grown_upper_slices, last_box); + //set the clip to a virtual "second perimeter" fill_clip = offset_ex(last, -double(ext_perimeter_spacing)); // get the real top surface @@ -856,13 +1021,15 @@ void PerimeterGenerator::process_classic() ExPolygons bridge_checker; // BBS: check whether surface be bridge or not if (this->lower_slices != NULL) { - grown_lower_slices =*this->lower_slices; + // BBS: get the Polygons below the polygon this layer + Polygons lower_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*this->lower_slices, last_box); + double bridge_offset = std::max(double(ext_perimeter_spacing), (double(perimeter_width))); - bridge_checker = offset_ex(diff_ex(last, grown_lower_slices, ApplySafetyOffset::Yes), 1.5 * bridge_offset); + bridge_checker = offset_ex(diff_ex(last, lower_polygons_series_clipped, ApplySafetyOffset::Yes), 1.5 * bridge_offset); } ExPolygons delete_bridge = diff_ex(last, bridge_checker, ApplySafetyOffset::Yes); - ExPolygons top_polygons = diff_ex(delete_bridge, grown_upper_slices, ApplySafetyOffset::Yes); + ExPolygons top_polygons = diff_ex(delete_bridge, upper_polygons_series_clipped, ApplySafetyOffset::Yes); //get the not-top surface, from the "real top" but enlarged by external_infill_margin (and the min_width_top_surface we removed a bit before) ExPolygons temp_gap = diff_ex(top_polygons, fill_clip); ExPolygons inner_polygons = diff_ex(last, @@ -997,7 +1164,7 @@ void PerimeterGenerator::process_classic() for (ExPolygon& ex : gaps_ex) { //BBS: Use DP simplify to avoid duplicated points and accelerate medial-axis calculation as well. ex.douglas_peucker(surface_simplify_resolution); - ex.medial_axis(max, min, &polylines); + ex.medial_axis(min, max, &polylines); } #ifdef GAPS_OF_PERIMETER_DEBUG_TO_SVG @@ -1124,6 +1291,9 @@ void PerimeterGenerator::process_arachne() m_lower_slices_polygons = offset(*this->lower_slices, float(scale_(+nozzle_diameter / 2))); } + + // BBS: don't simplify too much which influence arc fitting when export gcode if arc_fitting is enabled + double surface_simplify_resolution = (print_config->enable_arc_fitting && this->config->fuzzy_skin == FuzzySkinType::None) ? 0.2 * m_scaled_resolution : m_scaled_resolution; // we need to process each island separately because we might have different // extra perimeters for each one for (const Surface& surface : this->slices->surfaces) { @@ -1137,7 +1307,7 @@ void PerimeterGenerator::process_arachne() // BBS: set the topmost layer to be one wall if (loop_number > 0 && config->only_one_wall_top && this->upper_slices == nullptr) loop_number = 0; - ExPolygons last = offset_ex(surface.expolygon.simplify_p(m_scaled_resolution), + ExPolygons last = offset_ex(surface.expolygon.simplify_p(surface_simplify_resolution), config->precise_outer_wall ? -float(ext_perimeter_width / 2. - bead_width_0 / 2.) : -float(ext_perimeter_width / 2. - ext_perimeter_spacing / 2.)); Polygons last_p = to_polygons(last); diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index b3f4cccb97..80644073ee 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -578,6 +578,9 @@ namespace cereal { template void load(Archive& archive, Slic3r::Matrix2f &m) { archive.loadBinary((char*)m.data(), sizeof(float) * 4); } template void save(Archive& archive, Slic3r::Matrix2f &m) { archive.saveBinary((char*)m.data(), sizeof(float) * 4); } + + template void load(Archive &archive, Slic3r::Transform3d &m) { archive.loadBinary((char *) m.data(), sizeof(double) * 16); } + template void save(Archive &archive, const Slic3r::Transform3d &m) { archive.saveBinary((char *) m.data(), sizeof(double) * 16); } } // To be able to use Vec<> and Mat<> in range based for loops: diff --git a/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp index 1ecd4537e3..5254157917 100644 --- a/src/libslic3r/Polygon.cpp +++ b/src/libslic3r/Polygon.cpp @@ -6,6 +6,17 @@ namespace Slic3r { +double Polygon::length() const +{ + double l = 0; + if (this->points.size() > 1) { + l = (this->points.back() - this->points.front()).cast().norm(); + for (size_t i = 1; i < this->points.size(); ++ i) + l += (this->points[i] - this->points[i - 1]).cast().norm(); + } + return l; +} + Lines Polygon::lines() const { return to_lines(*this); @@ -88,36 +99,11 @@ void Polygon::douglas_peucker(double tolerance) this->points = std::move(p); } -// Does an unoriented polygon contain a point? -// Tested by counting intersections along a horizontal line. -bool Polygon::contains(const Point &point) const -{ - // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html - bool result = false; - Points::const_iterator i = this->points.begin(); - Points::const_iterator j = this->points.end() - 1; - for (; i != this->points.end(); j = i++) { - //FIXME this test is not numerically robust. Particularly, it does not handle horizontal segments at y == point(1) well. - // Does the ray with y == point(1) intersect this line segment? -#if 1 - if ( (((*i)(1) > point(1)) != ((*j)(1) > point(1))) - && ((double)point(0) < (double)((*j)(0) - (*i)(0)) * (double)(point(1) - (*i)(1)) / (double)((*j)(1) - (*i)(1)) + (double)(*i)(0)) ) - result = !result; -#else - if (((*i)(1) > point(1)) != ((*j)(1) > point(1))) { - // Orientation predicated relative to i-th point. - double orient = (double)(point(0) - (*i)(0)) * (double)((*j)(1) - (*i)(1)) - (double)(point(1) - (*i)(1)) * (double)((*j)(0) - (*i)(0)); - if (((*i)(1) > (*j)(1)) ? (orient > 0.) : (orient < 0.)) - result = !result; - } -#endif - } - return result; -} - -// this only works on CCW polygons as CW will be ripped out by Clipper's simplify_polygons() Polygons Polygon::simplify(double tolerance) const { + // Works on CCW polygons only, CW contour will be reoriented to CCW by Clipper's simplify_polygons()! + assert(this->is_counter_clockwise()); + // repeat first point at the end in order to apply Douglas-Peucker // on the whole polygon Points points = this->points; @@ -130,13 +116,6 @@ Polygons Polygon::simplify(double tolerance) const return simplify_polygons(pp); } -void Polygon::simplify(double tolerance, Polygons &polygons) const -{ - Polygons pp = this->simplify(tolerance); - polygons.reserve(polygons.size() + pp.size()); - polygons.insert(polygons.end(), pp.begin(), pp.end()); -} - // Only call this on convex polygons or it will return invalid results void Polygon::triangulate_convex(Polygons* polygons) const { @@ -171,50 +150,125 @@ Point Polygon::centroid() const return Point(Vec2d(c / (3. * area_sum))); } -// find all concave vertices (i.e. having an internal angle greater than the supplied angle) -// (external = right side, thus we consider ccw orientation) -Points Polygon::concave_points(double angle) const +bool Polygon::intersection(const Line &line, Point *intersection) const { - Points points; - angle = 2. * PI - angle + EPSILON; - - // check whether first point forms a concave angle - if (this->points.front().ccw_angle(this->points.back(), *(this->points.begin()+1)) <= angle) - points.push_back(this->points.front()); - - // check whether points 1..(n-1) form concave angles - for (Points::const_iterator p = this->points.begin()+1; p != this->points.end()-1; ++ p) - if (p->ccw_angle(*(p-1), *(p+1)) <= angle) - points.push_back(*p); - - // check whether last point forms a concave angle - if (this->points.back().ccw_angle(*(this->points.end()-2), this->points.front()) <= angle) - points.push_back(this->points.back()); - - return points; + if (this->points.size() < 2) + return false; + if (Line(this->points.front(), this->points.back()).intersection(line, intersection)) + return true; + for (size_t i = 1; i < this->points.size(); ++ i) + if (Line(this->points[i - 1], this->points[i]).intersection(line, intersection)) + return true; + return false; } -// find all convex vertices (i.e. having an internal angle smaller than the supplied angle) -// (external = right side, thus we consider ccw orientation) -Points Polygon::convex_points(double angle) const +bool Polygon::first_intersection(const Line& line, Point* intersection) const { - Points points; - angle = 2*PI - angle - EPSILON; - - // check whether first point forms a convex angle - if (this->points.front().ccw_angle(this->points.back(), *(this->points.begin()+1)) >= angle) - points.push_back(this->points.front()); - - // check whether points 1..(n-1) form convex angles - for (Points::const_iterator p = this->points.begin()+1; p != this->points.end()-1; ++p) { - if (p->ccw_angle(*(p-1), *(p+1)) >= angle) points.push_back(*p); + if (this->points.size() < 2) + return false; + + bool found = false; + double dmin = 0.; + Line l(this->points.back(), this->points.front()); + for (size_t i = 0; i < this->points.size(); ++ i) { + l.b = this->points[i]; + Point ip; + if (l.intersection(line, &ip)) { + if (! found) { + found = true; + dmin = (line.a - ip).cast().squaredNorm(); + *intersection = ip; + } else { + double d = (line.a - ip).cast().squaredNorm(); + if (d < dmin) { + dmin = d; + *intersection = ip; + } + } + } + l.a = l.b; + } + return found; +} + +bool Polygon::intersections(const Line &line, Points *intersections) const +{ + if (this->points.size() < 2) + return false; + + size_t intersections_size = intersections->size(); + Line l(this->points.back(), this->points.front()); + for (size_t i = 0; i < this->points.size(); ++ i) { + l.b = this->points[i]; + Point intersection; + if (l.intersection(line, &intersection)) + intersections->emplace_back(std::move(intersection)); + l.a = l.b; + } + return intersections->size() > intersections_size; +} +bool Polygon::overlaps(const Polygons& other) const +{ + if (this->empty() || other.empty()) + return false; + Polylines pl_out = intersection_pl(to_polylines(other), *this); + + // See unit test SCENARIO("Clipper diff with polyline", "[Clipper]") + // for in which case the intersection_pl produces any intersection. + return !pl_out.empty() || + // If *this is completely inside other, then pl_out is empty, but the expolygons overlap. Test for that situation. + std::any_of(other.begin(), other.end(), [this](auto& poly) {return poly.contains(this->points.front()); }); +} +// Filter points from poly to the output with the help of FilterFn. +// filter function receives two vectors: +// v1: this_point - previous_point +// v2: next_point - this_point +// and returns true if the point is to be copied to the output. +template +Points filter_points_by_vectors(const Points &poly, FilterFn filter) +{ + // Last point is the first point visited. + Point p1 = poly.back(); + // Previous vector to p1. + Vec2d v1 = (p1 - *(poly.end() - 2)).cast(); + + Points out; + for (Point p2 : poly) { + // p2 is next point to the currently visited point p1. + Vec2d v2 = (p2 - p1).cast(); + if (filter(v1, v2)) + out.emplace_back(p2); + v1 = v2; + p1 = p2; } - // check whether last point forms a convex angle - if (this->points.back().ccw_angle(*(this->points.end()-2), this->points.front()) >= angle) - points.push_back(this->points.back()); - - return points; + return out; +} + +template +Points filter_convex_concave_points_by_angle_threshold(const Points &poly, double angle_threshold, ConvexConcaveFilterFn convex_concave_filter) +{ + assert(angle_threshold >= 0.); + if (angle_threshold < EPSILON) { + double cos_angle = cos(angle_threshold); + return filter_points_by_vectors(poly, [convex_concave_filter, cos_angle](const Vec2d &v1, const Vec2d &v2){ + return convex_concave_filter(v1, v2) && v1.normalized().dot(v2.normalized()) < cos_angle; + }); + } else { + return filter_points_by_vectors(poly, [convex_concave_filter](const Vec2d &v1, const Vec2d &v2){ + return convex_concave_filter(v1, v2); + }); + } +} + +Points Polygon::convex_points(double angle_threshold) const +{ + return filter_convex_concave_points_by_angle_threshold(this->points, angle_threshold, [](const Vec2d &v1, const Vec2d &v2){ return cross2(v1, v2) > 0.; }); +} + +Points Polygon::concave_points(double angle_threshold) const +{ + return filter_convex_concave_points_by_angle_threshold(this->points, angle_threshold, [](const Vec2d &v1, const Vec2d &v2){ return cross2(v1, v2) < 0.; }); } // Projection of a point onto the polygon. @@ -540,4 +594,74 @@ void remove_collinear(Polygons &polys) remove_collinear(poly); } +Polygons polygons_simplify(const Polygons &source_polygons, double tolerance) +{ + Polygons out; + out.reserve(source_polygons.size()); + for (const Polygon &source_polygon : source_polygons) { + // Run Douglas / Peucker simplification algorithm on an open polyline (by repeating the first point at the end of the polyline), + Points simplified = MultiPoint::_douglas_peucker(to_polyline(source_polygon).points, tolerance); + // then remove the last (repeated) point. + simplified.pop_back(); + // Simplify the decimated contour by ClipperLib. + bool ccw = ClipperLib::Area(simplified) > 0.; + for (Points &path : ClipperLib::SimplifyPolygons(ClipperUtils::SinglePathProvider(simplified), ClipperLib::pftNonZero)) { + if (! ccw) + // ClipperLib likely reoriented negative area contours to become positive. Reverse holes back to CW. + std::reverse(path.begin(), path.end()); + out.emplace_back(std::move(path)); + } + } + return out; } + +// Do polygons match? If they match, they must have the same topology, +// however their contours may be rotated. +bool polygons_match(const Polygon &l, const Polygon &r) +{ + if (l.size() != r.size()) + return false; + auto it_l = std::find(l.points.begin(), l.points.end(), r.points.front()); + if (it_l == l.points.end()) + return false; + auto it_r = r.points.begin(); + for (; it_l != l.points.end(); ++ it_l, ++ it_r) + if (*it_l != *it_r) + return false; + it_l = l.points.begin(); + for (; it_r != r.points.end(); ++ it_l, ++ it_r) + if (*it_l != *it_r) + return false; + return true; +} + +bool overlaps(const Polygons& polys1, const Polygons& polys2) +{ + for (const Polygon& poly1 : polys1) { + if (poly1.overlaps(polys2)) + return true; + } + return false; +} + +bool contains(const Polygon &polygon, const Point &p, bool border_result) +{ + if (const int poly_count_inside = ClipperLib::PointInPolygon(p, polygon.points); + poly_count_inside == -1) + return border_result; + else + return (poly_count_inside % 2) == 1; +} + +bool contains(const Polygons &polygons, const Point &p, bool border_result) +{ + int poly_count_inside = 0; + for (const Polygon &poly : polygons) { + const int is_inside_this_poly = ClipperLib::PointInPolygon(p, poly.points); + if (is_inside_this_poly == -1) + return border_result; + poly_count_inside += is_inside_this_poly; + } + return (poly_count_inside % 2) == 1; +} +} \ No newline at end of file diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp index 16126f94a0..7da63c2cc5 100644 --- a/src/libslic3r/Polygon.hpp +++ b/src/libslic3r/Polygon.hpp @@ -15,11 +15,14 @@ using Polygons = std::vector; using PolygonPtrs = std::vector; using ConstPolygonPtrs = std::vector; +// Returns true if inside. Returns border_result if on boundary. +bool contains(const Polygon& polygon, const Point& p, bool border_result = true); +bool contains(const Polygons& polygons, const Point& p, bool border_result = true); + class Polygon : public MultiPoint { public: Polygon() = default; - virtual ~Polygon() = default; explicit Polygon(const Points &points) : MultiPoint(points) {} Polygon(std::initializer_list points) : MultiPoint(points) {} Polygon(const Polygon &other) : MultiPoint(other.points) {} @@ -38,9 +41,10 @@ public: const Point& operator[](Points::size_type idx) const { return this->points[idx]; } // last point == first point for polygons - const Point& last_point() const override { return this->points.front(); } + const Point& last_point() const { return this->points.front(); } - Lines lines() const override; + double length() const; + Lines lines() const; Polyline split_at_vertex(const Point &point) const; // Split a closed polygon into an open polyline, with the split point duplicated at both ends. Polyline split_at_index(int index) const; @@ -58,15 +62,27 @@ public: void douglas_peucker(double tolerance); // Does an unoriented polygon contain a point? - // Tested by counting intersections along a horizontal line. - bool contains(const Point &point) const; + bool contains(const Point &point) const { return Slic3r::contains(*this, point, true); } + // Approximate on boundary test. + bool on_boundary(const Point &point, double eps) const + { return (this->point_projection(point) - point).cast().squaredNorm() < eps * eps; } + + // Works on CCW polygons only, CW contour will be reoriented to CCW by Clipper's simplify_polygons()! Polygons simplify(double tolerance) const; - void simplify(double tolerance, Polygons &polygons) const; void densify(float min_length, std::vector* lengths = nullptr); void triangulate_convex(Polygons* polygons) const; Point centroid() const; - Points concave_points(double angle = PI) const; - Points convex_points(double angle = PI) const; + + bool intersection(const Line& line, Point* intersection) const; + bool first_intersection(const Line& line, Point* intersection) const; + bool intersections(const Line& line, Points* intersections) const; + bool overlaps(const Polygons& other) const; + + // Considering CCW orientation of this polygon, find all convex resp. concave points + // with the angle at the vertex larger than a threshold. + // Zero angle_threshold means to accept all convex resp. concave points. + Points convex_points(double angle_threshold = 0.) const; + Points concave_points(double angle_threshold = 0.) const; // Projection of a point onto the polygon. Point point_projection(const Point &point) const; std::vector parameter_by_length() const; @@ -136,14 +152,7 @@ inline void polygons_append(Polygons &dst, Polygons &&src) } } -inline Polygons polygons_simplify(const Polygons &polys, double tolerance) -{ - Polygons out; - out.reserve(polys.size()); - for (const Polygon &p : polys) - polygons_append(out, p.simplify(tolerance)); - return out; -} +Polygons polygons_simplify(const Polygons &polys, double tolerance); inline void polygons_rotate(Polygons &polys, double angle) { @@ -164,13 +173,16 @@ inline Points to_points(const Polygon &poly) return poly.points; } +inline size_t count_points(const Polygons &polys) { + size_t n_points = 0; + for (const auto &poly: polys) n_points += poly.points.size(); + return n_points; +} + inline Points to_points(const Polygons &polys) { - size_t n_points = 0; - for (size_t i = 0; i < polys.size(); ++ i) - n_points += polys[i].points.size(); Points points; - points.reserve(n_points); + points.reserve(count_points(polys)); for (const Polygon &poly : polys) append(points, poly.points); return points; @@ -190,11 +202,8 @@ inline Lines to_lines(const Polygon &poly) inline Lines to_lines(const Polygons &polys) { - size_t n_lines = 0; - for (size_t i = 0; i < polys.size(); ++ i) - n_lines += polys[i].points.size(); Lines lines; - lines.reserve(n_lines); + lines.reserve(count_points(polys)); for (size_t i = 0; i < polys.size(); ++ i) { const Polygon &poly = polys[i]; for (Points::const_iterator it = poly.points.begin(); it != poly.points.end()-1; ++it) @@ -204,18 +213,22 @@ inline Lines to_lines(const Polygons &polys) return lines; } -inline Polylines to_polylines(const Polygons &polys) +inline Polyline to_polyline(const Polygon &polygon) { - Polylines polylines; - polylines.assign(polys.size(), Polyline()); - size_t idx = 0; - for (Polygons::const_iterator it = polys.begin(); it != polys.end(); ++ it) { - Polyline &pl = polylines[idx ++]; - pl.points = it->points; - pl.points.push_back(it->points.front()); - } - assert(idx == polylines.size()); - return polylines; + Polyline out; + out.points.reserve(polygon.size() + 1); + out.points.assign(polygon.points.begin(), polygon.points.end()); + out.points.push_back(polygon.points.front()); + return out; +} + +inline Polylines to_polylines(const Polygons &polygons) +{ + Polylines out; + out.reserve(polygons.size()); + for (const Polygon &polygon : polygons) + out.emplace_back(to_polyline(polygon)); + return out; } inline Polylines to_polylines(Polygons &&polys) @@ -223,10 +236,10 @@ inline Polylines to_polylines(Polygons &&polys) Polylines polylines; polylines.assign(polys.size(), Polyline()); size_t idx = 0; - for (Polygons::const_iterator it = polys.begin(); it != polys.end(); ++ it) { + for (auto it = polys.begin(); it != polys.end(); ++ it) { Polyline &pl = polylines[idx ++]; pl.points = std::move(it->points); - pl.points.push_back(it->points.front()); + pl.points.push_back(pl.points.front()); } assert(idx == polylines.size()); return polylines; @@ -245,11 +258,16 @@ inline Polygons to_polygons(std::vector &&paths) { Polygons out; out.reserve(paths.size()); - for (const Points &path : paths) + for (Points &path : paths) out.emplace_back(std::move(path)); return out; } +// Do polygons match? If they match, they must have the same topology, +// however their contours may be rotated. +bool polygons_match(const Polygon &l, const Polygon &r); + +bool overlaps(const Polygons& polys1, const Polygons& polys2); } // Slic3r // start Boost diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp index df28742360..383e36604a 100644 --- a/src/libslic3r/Polyline.cpp +++ b/src/libslic3r/Polyline.cpp @@ -2,7 +2,6 @@ #include "Polyline.hpp" #include "Exception.hpp" #include "ExPolygon.hpp" -#include "ExPolygonCollection.hpp" #include "Line.hpp" #include "Polygon.hpp" #include diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp index ac00888d31..a9d919a22d 100644 --- a/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -234,6 +234,10 @@ public: std::reverse(this->width.begin(), this->width.end()); std::swap(this->endpoints.first, this->endpoints.second); } + void clear() { + Polyline::clear(); + width.clear(); + } std::vector width; std::pair endpoints; diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 499f4e1d1c..4753e1dad9 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -725,7 +725,7 @@ static std::vector s_Preset_print_options { "raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion", "support_base_pattern", "support_base_pattern_spacing", "support_expansion", "support_style", // BBS - //"independent_support_layer_height", + "independent_support_layer_height", "support_angle", "support_interface_top_layers", "support_interface_bottom_layers", "support_interface_pattern", "support_interface_spacing", "support_interface_loop_pattern", "support_top_z_distance", "support_on_build_plate_only","support_critical_regions_only", "bridge_no_support", "thick_bridges", "max_bridge_length", "print_sequence", @@ -747,16 +747,16 @@ static std::vector s_Preset_print_options { "initial_layer_infill_speed", "only_one_wall_top", "timelapse_type", "internal_bridge_support_thickness", "wall_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle", - "wall_distribution_count", "min_feature_size", "min_bead_width", + "wall_distribution_count", "min_feature_size", "min_bead_width", "post_process", // SoftFever - "small_perimeter_speed", "small_perimeter_threshold","bridge_angle", "filter_out_gap_fill", "post_process", "travel_acceleration","inner_wall_acceleration", + "small_perimeter_speed", "small_perimeter_threshold","bridge_angle", "filter_out_gap_fill", "travel_acceleration","inner_wall_acceleration", "default_jerk", "outer_wall_jerk", "inner_wall_jerk", "infill_jerk", "top_surface_jerk", "initial_layer_jerk","travel_jerk", "top_solid_infill_flow_ratio","bottom_solid_infill_flow_ratio","only_one_wall_first_layer", "print_flow_ratio", "seam_gap", - "role_based_wipe_speed", "wipe_speed", "accel_to_decel_enable", "accel_to_decel_factor", "wipe_on_loops", - "bridge_density", "precise_outer_wall", "overhang_speed_classic", "bridge_acceleration", - "sparse_infill_acceleration", "internal_solid_infill_acceleration", "tree_support_adaptive_layer_height", "tree_support_auto_brim", - "tree_support_brim_width", "gcode_comments", "gcode_label_objects", - "initial_layer_travel_speed", "exclude_object" + "role_based_wipe_speed", "wipe_speed", "accel_to_decel_enable", "accel_to_decel_factor", "wipe_on_loops", + "bridge_density", "precise_outer_wall", "overhang_speed_classic", "bridge_acceleration", + "sparse_infill_acceleration", "internal_solid_infill_acceleration", "tree_support_adaptive_layer_height", "tree_support_auto_brim", + "tree_support_brim_width", "gcode_comments", "gcode_label_objects", + "initial_layer_travel_speed", "exclude_object" }; diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 609da0a945..d2b2f4051f 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -241,12 +241,12 @@ PresetsConfigSubstitutions PresetBundle::load_presets(AppConfig &config, Forward //BBS: change system config to json std::tie(substitutions, errors_cummulative) = this->load_system_presets_from_json(substitution_rule); - // Load default user presets always - load_user_presets(DEFAULT_USER_FOLDER_NAME, substitution_rule); // BBS load preset from user's folder, load system default if // BBS: change directories by design std::string dir_user_presets = config.get("preset_folder"); - if (!dir_user_presets.empty()) { + if (dir_user_presets.empty()) { + load_user_presets(DEFAULT_USER_FOLDER_NAME, substitution_rule); + } else { load_user_presets(dir_user_presets, substitution_rule); } @@ -650,24 +650,26 @@ PresetsConfigSubstitutions PresetBundle::import_presets(std::vector BOOST_LOG_TRIVIAL(warning) << "Preset type is unknown, not loading: " << name; continue; } + if (overwrite == 0) overwrite = 1; if (auto p = collection->find_preset(name, false)) { if (p->is_default || p->is_system) { BOOST_LOG_TRIVIAL(warning) << "Preset already present and is system preset, not loading: " << name; continue; } overwrite = override_confirm(name); - if (overwrite == 0 || overwrite == 2) { - BOOST_LOG_TRIVIAL(warning) << "Preset already present, not loading: " << name; - continue; - } + } + if (overwrite == 0 || overwrite == 2) { + BOOST_LOG_TRIVIAL(warning) << "Preset already present, not loading: " << name; + continue; } DynamicPrintConfig new_config; Preset * inherit_preset = nullptr; ConfigOption *inherits_config = config.option(BBL_JSON_KEY_INHERITS); + std::string inherits_value; if (inherits_config) { ConfigOptionString *option_str = dynamic_cast(inherits_config); - std::string inherits_value = option_str->value; + inherits_value = option_str->value; inherit_preset = collection->find_preset(inherits_value, false, true); } if (inherit_preset) { @@ -684,6 +686,7 @@ PresetsConfigSubstitutions PresetBundle::import_presets(std::vector Preset &preset = collection->load_preset(collection->path_from_name(name), name, std::move(new_config), false); preset.is_external = true; preset.version = *version; + inherit_preset = collection->find_preset(inherits_value, false, true); // pointer maybe wrong after insert, redo find if (inherit_preset) preset.base_id = inherit_preset->setting_id; Preset::normalize(preset.config); @@ -863,7 +866,7 @@ void PresetBundle::remove_users_preset(AppConfig &config, std::mapis_user() && !it->user_id.empty() && it->user_id.compare(preset_folder_user_id) == 0 && check_removed(*it)) { + if (it->is_user() && it->user_id.compare(preset_folder_user_id) == 0 && check_removed(*it)) { BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(":printers erase %1%, type %2%, user_id %3%") % it->name % Preset::get_type_string(it->type) % it->user_id; if (it->name == printer_selected_preset_name) need_reset_printer_preset = true; @@ -1306,7 +1309,7 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p if (config.has("presets", "filament_colors")) { boost::algorithm::split(filament_colors, config.get("presets", "filament_colors"), boost::algorithm::is_any_of(",")); } - filament_colors.resize(filament_presets.size()); + filament_colors.resize(filament_presets.size(), "#00AE42"); project_config.option("filament_colour")->values = filament_colors; std::vector matrix; if (config.has("presets", "flush_volumes_matrix")) { @@ -1319,6 +1322,11 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p auto flush_volumes_vector = matrix | boost::adaptors::transformed(boost::lexical_cast); project_config.option("flush_volumes_vector")->values = std::vector(flush_volumes_vector.begin(), flush_volumes_vector.end()); } + if (config.has("app", "flush_multiplier")) { + std::string str_flush_multiplier = config.get("app", "flush_multiplier"); + if (!str_flush_multiplier.empty()) + project_config.option("flush_multiplier")->set(new ConfigOptionFloat(std::stof(str_flush_multiplier))); + } // Update visibility of presets based on their compatibility with the active printer. // Always try to select a compatible print and filament preset to the current printer preset, @@ -1393,6 +1401,9 @@ void PresetBundle::export_selections(AppConfig &config) config.set("presets", "flush_volumes_vector", flush_volumes_vector); config.set("presets", PRESET_PRINTER_NAME, printers.get_selected_preset_name()); + + auto flush_multi_opt = project_config.option("flush_multiplier"); + config.set("flush_multiplier", std::to_string(flush_multi_opt ? flush_multi_opt->getFloat() : 1.0f)); // BBS //config.set("presets", "sla_print", sla_prints.get_selected_preset_name()); //config.set("presets", "sla_material", sla_materials.get_selected_preset_name()); @@ -3510,6 +3521,7 @@ std::vector PresetBundle::export_current_configs(const std::string if ((preset->is_system && !export_system_settings) || preset->is_default) continue; std::string file = path + "/" + preset->name + ".json"; + if (overwrite == 0) overwrite = 1; if (boost::filesystem::exists(file) && overwrite < 2) { overwrite = override_confirm(preset->name); if (overwrite == 0 || overwrite == 2) diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 76b8208bda..ab57dd13d3 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -27,6 +27,10 @@ //BBS: add json support #include "nlohmann/json.hpp" +#include "GCode/ConflictChecker.hpp" + +#include + using namespace nlohmann; // Mark string for localization and translate. @@ -227,7 +231,8 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n steps.emplace_back(psWipeTower); steps.emplace_back(psSkirtBrim); } else if (opt_key == "filament_soluble" - || opt_key == "filament_is_support") { + || opt_key == "filament_is_support" + || opt_key == "independent_support_layer_height") { steps.emplace_back(psWipeTower); // Soluble support interface / non-soluble base interface produces non-soluble interface layers below soluble interface layers. // Thus switching between soluble / non-soluble interface layer material may require recalculation of supports. @@ -366,10 +371,11 @@ std::vector Print::extruders(bool conside_custom_gcode) const if (conside_custom_gcode) { //BBS + int num_extruders = m_config.filament_colour.size(); for (auto plate_data : m_model.plates_custom_gcodes) { for (auto item : plate_data.second.gcodes) { - if (item.type == CustomGCode::Type::ToolChange) - extruders.push_back((unsigned int)item.extruder); + if (item.type == CustomGCode::Type::ToolChange && item.extruder <= num_extruders) + extruders.push_back((unsigned int)(item.extruder - 1)); } } } @@ -488,14 +494,17 @@ StringObjectException Print::sequential_print_clearance_valid(const Print &print // Now we check that no instance of convex_hull intersects any of the previously checked object instances. for (const PrintInstance &instance : print_object->instances()) { Polygon convex_hull_no_offset = convex_hull0, convex_hull; - convex_hull = offset(convex_hull_no_offset, + auto tmp = offset(convex_hull_no_offset, // Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects // exactly by satisfying the extruder_clearance_radius, this test will not trigger collision. float(scale_(0.5 * print.config().extruder_clearance_radius.value - EPSILON)), - jtRound, scale_(0.1)).front(); - // instance.shift is a position of a centered object, while model object may not be centered. - // Convert the shift from the PrintObject's coordinates into ModelObject's coordinates by removing the centering offset. - convex_hull.translate(instance.shift - print_object->center_offset()); + jtRound, scale_(0.1)); + if (!tmp.empty()) { // tmp may be empty due to clipper's bug, see STUDIO-2452 + convex_hull = tmp.front(); + // instance.shift is a position of a centered object, while model object may not be centered. + // Convert the shift from the PrintObject's coordinates into ModelObject's coordinates by removing the centering offset. + convex_hull.translate(instance.shift - print_object->center_offset()); + } convex_hull_no_offset.translate(instance.shift - print_object->center_offset()); //juedge the exclude area if (!intersection(exclude_polys, convex_hull_no_offset).empty()) { @@ -1136,6 +1145,13 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons* if (layer_height > min_nozzle_diameter) return {L("Layer height cannot exceed nozzle diameter"), object, "layer_height"}; + for (auto range : object->m_model_object->layer_config_ranges) { + double range_layer_height = range.second.opt_float("layer_height"); + if (range_layer_height > object->m_slicing_params.max_layer_height || + range_layer_height < object->m_slicing_params.min_layer_height) + return { L("Layer height cannot exceed nozzle diameter"), nullptr, "layer_height" }; + } + // Validate extrusion widths. std::string err_msg; if (!validate_extrusion_width(object->config(), "line_width", layer_height, err_msg)) @@ -1475,14 +1491,20 @@ void Print::process(bool use_cache) for (int index = 0; index < object_count; index++) { PrintObject *obj = m_objects[index]; + bool found_shared = false; if (need_slicing_objects.find(obj) == need_slicing_objects.end()) { for (PrintObject *slicing_obj : need_slicing_objects) { if (is_print_object_the_same(obj, slicing_obj)) { obj->set_shared_object(slicing_obj); + found_shared = true; break; } } + if (!found_shared) { + BOOST_LOG_TRIVIAL(error) << boost::format("Also can not find the shared object, identify_id %1%")%obj->model_object()->instances[0]->loaded_id; + throw Slic3r::SlicingError("Can not find the cached data."); + } } } } @@ -1685,6 +1707,27 @@ void Print::process(bool use_cache) } } + // BBS + if(!m_no_check) + { + using Clock = std::chrono::high_resolution_clock; + auto startTime = Clock::now(); + std::optional wipe_tower_opt = {}; + if (this->has_wipe_tower()) { + m_fake_wipe_tower.set_pos({m_config.wipe_tower_x.get_at(m_plate_index), m_config.wipe_tower_y.get_at(m_plate_index)}); + wipe_tower_opt = std::make_optional(&m_fake_wipe_tower); + } + auto conflictRes = ConflictChecker::find_inter_of_lines_in_diff_objs(m_objects, wipe_tower_opt); + auto endTime = Clock::now(); + volatile double seconds = std::chrono::duration_cast(endTime - startTime).count() / (double) 1000; + BOOST_LOG_TRIVIAL(info) << "gcode path conflicts check takes " << seconds << " secs."; + + m_conflict_result = conflictRes; + if (conflictRes.has_value()) { + BOOST_LOG_TRIVIAL(error) << boost::format("gcode path conflicts found between %1% and %2%")%conflictRes.value()._objName1 %conflictRes.value()._objName2; + } + } + BOOST_LOG_TRIVIAL(info) << "Slicing process finished." << log_memory_info(); } @@ -1713,6 +1756,8 @@ std::string Print::export_gcode(const std::string& path_template, GCodeProcessor const Vec3d origin = this->get_plate_origin(); gcode.set_gcode_offset(origin(0), origin(1)); gcode.do_export(this, path.c_str(), result, thumbnail_cb); + //BBS + result->conflict_result = m_conflict_result; return path.c_str(); } @@ -2147,8 +2192,11 @@ void Print::_make_wipe_tower() m_wipe_tower_data.final_purge = Slic3r::make_unique( wipe_tower.tool_change((unsigned int)(-1))); - m_wipe_tower_data.used_filament = wipe_tower.get_used_filament(); + m_wipe_tower_data.used_filament = wipe_tower.get_used_filament(); m_wipe_tower_data.number_of_toolchanges = wipe_tower.get_number_of_toolchanges(); + const Vec3d origin = this->get_plate_origin(); + m_fake_wipe_tower.set_fake_extrusion_data(wipe_tower.position(), wipe_tower.width(), wipe_tower.get_height(), wipe_tower.get_layer_height(), m_wipe_tower_data.depth, + m_wipe_tower_data.brim_width, {scale_(origin.x()), scale_(origin.y())}); } // Generate a recommended G-code output file name based on the format template, default extension, and template parameters @@ -2251,14 +2299,18 @@ std::string PrintStatistics::finalize_output_path(const std::string &path_in) co #define JSON_EXPOLYGON "expolygon" #define JSON_ARC_FITTING "arc_fitting" #define JSON_OBJECT_NAME "name" -#define JSON_ARRANGE_ORDER "arrange_order" +#define JSON_IDENTIFY_ID "identify_id" #define JSON_LAYERS "layers" #define JSON_SUPPORT_LAYERS "support_layers" #define JSON_TREE_SUPPORT_LAYERS "tree_support_layers" #define JSON_LAYER_REGIONS "layer_regions" +#define JSON_FIRSTLAYER_GROUPS "first_layer_groups" +#define JSON_FIRSTLAYER_GROUP_ID "group_id" +#define JSON_FIRSTLAYER_GROUP_VOLUME_IDS "volume_ids" +#define JSON_FIRSTLAYER_GROUP_SLICES "slices" #define JSON_LAYER_PRINT_Z "print_z" #define JSON_LAYER_SLICE_Z "slice_z" @@ -2584,6 +2636,24 @@ static void to_json(json& j, const LayerRegion& layer_region) { return; } +static void to_json(json& j, const groupedVolumeSlices& first_layer_group) { + json volumes_json = json::array(), slices_json = json::array(); + j[JSON_FIRSTLAYER_GROUP_ID] = first_layer_group.groupId; + + for (const ObjectID& obj_id : first_layer_group.volume_ids) + { + volumes_json.push_back(obj_id.id); + } + j[JSON_FIRSTLAYER_GROUP_VOLUME_IDS] = std::move(volumes_json); + + for (const ExPolygon& slice_expolygon : first_layer_group.slices) { + json slice_expolygon_json = slice_expolygon; + + slices_json.push_back(std::move(slice_expolygon_json)); + } + j[JSON_FIRSTLAYER_GROUP_SLICES] = std::move(slices_json); +} + //load apis from json static void from_json(const json& j, Points& p_s) { int array_size = j.size(); @@ -2909,7 +2979,7 @@ void extract_support_layer(const json& support_layer_json, SupportLayer& support ExPolygon polygon; polygon = support_layer_json[JSON_SUPPORT_LAYER_ISLANDS][islands_index]; - support_layer.support_islands.expolygons.push_back(std::move(polygon)); + support_layer.support_islands.push_back(std::move(polygon)); } //support_fills @@ -2930,6 +3000,29 @@ void extract_support_layer(const json& support_layer_json, SupportLayer& support return; } +static void from_json(const json& j, groupedVolumeSlices& firstlayer_group) +{ + firstlayer_group.groupId = j[JSON_FIRSTLAYER_GROUP_ID]; + + int volume_count = j[JSON_FIRSTLAYER_GROUP_VOLUME_IDS].size(); + for (int volume_index = 0; volume_index < volume_count; volume_index++) + { + ObjectID obj_id; + + obj_id.id = j[JSON_FIRSTLAYER_GROUP_VOLUME_IDS][volume_index]; + firstlayer_group.volume_ids.push_back(std::move(obj_id)); + } + + int slices_count = j[JSON_FIRSTLAYER_GROUP_SLICES].size(); + for (int slice_index = 0; slice_index < slices_count; slice_index++) + { + ExPolygon polygon; + + polygon = j[JSON_FIRSTLAYER_GROUP_SLICES][slice_index]; + firstlayer_group.slices.push_back(std::move(polygon)); + } +} + int Print::export_cached_data(const std::string& directory, bool with_space) { int ret = 0; @@ -2987,18 +3080,19 @@ int Print::export_cached_data(const std::string& directory, bool with_space) BOOST_LOG_TRIVIAL(info) << boost::format("shared object %1%, skip directly")%model_obj->name; continue; } - BOOST_LOG_TRIVIAL(info) << boost::format("begin to dump object %1%")%model_obj->name; const PrintInstance &print_instance = obj->instances()[0]; const ModelInstance *model_instance = print_instance.model_instance; - int arrange_order = model_instance->arrange_order; - std::string file_name = directory +"/obj_"+std::to_string(arrange_order)+".json"; + size_t identify_id = (model_instance->loaded_id > 0)?model_instance->loaded_id: model_instance->id().id; + std::string file_name = directory +"/obj_"+std::to_string(identify_id)+".json"; + + BOOST_LOG_TRIVIAL(info) << boost::format("begin to dump object %1%, identify_id %2% to %3%")%model_obj->name %identify_id %file_name; try { - json root_json, layers_json = json::array(), support_layers_json = json::array(); + json root_json, layers_json = json::array(), support_layers_json = json::array(), first_layer_groups = json::array(); root_json[JSON_OBJECT_NAME] = model_obj->name; - root_json[JSON_ARRANGE_ORDER] = arrange_order; + root_json[JSON_IDENTIFY_ID] = identify_id; //export the layers std::vector layers_json_vector(obj->layer_count()); @@ -3043,7 +3137,7 @@ int Print::export_cached_data(const std::string& directory, bool with_space) support_layer_json[JSON_SUPPORT_LAYER_TYPE] = support_layer->support_type; //support_islands - for (const ExPolygon& support_island : support_layer->support_islands.expolygons) { + for (const ExPolygon& support_island : support_layer->support_islands) { json support_island_json = support_island; support_islands_json.push_back(std::move(support_island_json)); } @@ -3104,6 +3198,35 @@ int Print::export_cached_data(const std::string& directory, bool with_space) } // for each layer*/ root_json[JSON_SUPPORT_LAYERS] = std::move(support_layers_json); + const std::vector &first_layer_obj_groups = obj->firstLayerObjGroups(); + for (size_t s_group_index = 0; s_group_index < first_layer_obj_groups.size(); ++ s_group_index) { + groupedVolumeSlices group = first_layer_obj_groups[s_group_index]; + + //convert the id + for (ObjectID& obj_id : group.volume_ids) + { + const ModelVolume* currentModelVolumePtr = nullptr; + //BBS: support shared object logic + const PrintObject* shared_object = obj->get_shared_object(); + if (!shared_object) + shared_object = obj; + const ModelVolumePtrs& volumes_ptr = shared_object->model_object()->volumes; + size_t volume_count = volumes_ptr.size(); + for (size_t index = 0; index < volume_count; index ++) { + currentModelVolumePtr = volumes_ptr[index]; + if (currentModelVolumePtr->id() == obj_id) { + obj_id.id = index; + break; + } + } + } + + json first_layer_group_json; + + first_layer_group_json = group; + first_layer_groups.push_back(std::move(first_layer_group_json)); + } + root_json[JSON_FIRSTLAYER_GROUPS] = std::move(first_layer_groups); filename_vector.push_back(file_name); json_vector.push_back(std::move(root_json)); @@ -3183,12 +3306,14 @@ int Print::load_cached_data(const std::string& directory) obj->clear_layers(); obj->clear_support_layers(); - int arrange_order = model_instance->arrange_order; - if (arrange_order <= 0) { - BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(": object %1% has invalid arrange_order %2%, can not load cached_data")%model_obj->name %arrange_order; - continue; + int identify_id = model_instance->loaded_id; + if (identify_id <= 0) { + //for old 3mf + identify_id = model_instance->id().id; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(": object %1%'s loaded_id is 0, need to use the instance_id %2%")%model_obj->name %identify_id; + //continue; } - std::string file_name = directory +"/obj_"+std::to_string(arrange_order)+".json"; + std::string file_name = directory +"/obj_"+std::to_string(identify_id)+".json"; if (!fs::exists(file_name)) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__<> root_json; std::string name = root_json.at(JSON_OBJECT_NAME); - int order = root_json.at(JSON_ARRANGE_ORDER); - int layer_count = 0, support_layer_count = 0; + int identify_id = root_json.at(JSON_IDENTIFY_ID); + int layer_count = 0, support_layer_count = 0, firstlayer_group_count = 0; layer_count = root_json[JSON_LAYERS].size(); support_layer_count = root_json[JSON_SUPPORT_LAYERS].size(); + firstlayer_group_count = root_json[JSON_FIRSTLAYER_GROUPS].size(); - BOOST_LOG_TRIVIAL(info) << __FUNCTION__<& firstlayer_objgroups = obj->firstLayerObjGroupsMod(); + for (int index = 0; index < firstlayer_group_count; index++) + { + json& firstlayer_group_json = root_json[JSON_FIRSTLAYER_GROUPS][index]; + groupedVolumeSlices firstlayer_group = firstlayer_group_json; + //convert the id + for (ObjectID& obj_id : firstlayer_group.volume_ids) + { + ModelVolume* currentModelVolumePtr = nullptr; + ModelVolumePtrs& volumes_ptr = obj->model_object()->volumes; + size_t volume_count = volumes_ptr.size(); + if (obj_id.id < volume_count) { + currentModelVolumePtr = volumes_ptr[obj_id.id]; + obj_id = currentModelVolumePtr->id(); + } + else { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< boost::format(": can not find volume_id %1% from object file %2% in firstlayer groups, volume_count %3%!") + %obj_id.id %object_filenames[obj_index].first %volume_count; + return CLI_IMPORT_CACHE_LOAD_FAILED; + } + } + firstlayer_objgroups.push_back(std::move(firstlayer_group)); + } + count ++; BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(": load object %1% from %2% successfully.")%count%object_filenames[obj_index].first; } diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 9a63afea36..96c361d7eb 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -52,6 +52,7 @@ struct groupedVolumeSlices enum SupportNecessaryType { NoNeedSupp=0, SharpTail, + Cantilever, LargeOverhang, }; @@ -80,7 +81,8 @@ enum PrintStep { // should be refreshed. psSlicingFinished = psSkirtBrim, psGCodeExport, - psCount, + psConflictCheck, + psCount }; enum PrintObjectStep { @@ -528,6 +530,58 @@ private: static bool infill_only_where_needed; }; +struct FakeWipeTower +{ + // generate fake extrusion + Vec2f pos; + float width; + float height; + float layer_height; + float depth; + float brim_width; + Vec2d plate_origin; + + void set_fake_extrusion_data(Vec2f p, float w, float h, float lh, float d, float bd, Vec2d o) + { + pos = p; + width = w; + height = h; + layer_height = lh; + depth = d; + brim_width = bd; + plate_origin = o; + } + + void set_pos(Vec2f p) { pos = p; } + + std::vector getFakeExtrusionPathsFromWipeTower() const + { + float h = height; + float lh = layer_height; + int d = scale_(depth); + int w = scale_(width); + int bd = scale_(brim_width); + Point minCorner = {scale_(pos.x()), scale_(pos.y())}; + Point maxCorner = {minCorner.x() + w, minCorner.y() + d}; + + std::vector paths; + for (float hh = 0.f; hh < h; hh += lh) { + ExtrusionPath path(ExtrusionRole::erWipeTower, 0.0, 0.0, lh); + path.polyline = {minCorner, {maxCorner.x(), minCorner.y()}, maxCorner, {minCorner.x(), maxCorner.y()}, minCorner}; + paths.push_back({path}); + + if (hh == 0.f) { // add brim + ExtrusionPath fakeBrim(ExtrusionRole::erBrim, 0.0, 0.0, lh); + Point wtbminCorner = {minCorner - Point{bd, bd}}; + Point wtbmaxCorner = {maxCorner + Point{bd, bd}}; + fakeBrim.polyline = {wtbminCorner, {wtbmaxCorner.x(), wtbminCorner.y()}, wtbmaxCorner, {wtbminCorner.x(), wtbmaxCorner.y()}, wtbminCorner}; + paths.back().push_back(fakeBrim); + } + } + return paths; + } +}; + struct WipeTowerData { // Following section will be consumed by the GCodeGenerator. @@ -736,9 +790,19 @@ public: int get_modified_count() const {return m_modified_count;} //BBS: add status for whether support used bool is_support_used() const {return m_support_used;} + std::string get_conflict_string() const + { + std::string result; + if (m_conflict_result) { + result = "Found gcode path conflicts between object " + m_conflict_result.value()._objName1 + " and " + m_conflict_result.value()._objName2; + } + + return result; + } //BBS static StringObjectException sequential_print_clearance_valid(const Print &print, Polygons *polygons = nullptr, std::vector>* height_polygons = nullptr); + ConflictResultOpt get_conflict_result() const { return m_conflict_result; } // Return 4 wipe tower corners in the world coordinates (shifted and rotated), including the wipe tower brim. std::vector first_layer_wipe_tower_corners(bool check_wipe_tower_existance=true) const; @@ -804,6 +868,9 @@ private: Vec3d m_origin; //BBS: modified_count int m_modified_count {0}; + //BBS + ConflictResultOpt m_conflict_result; + FakeWipeTower m_fake_wipe_tower; //SoftFever: calibration Calib_Params m_calib_params; diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index 79a9143eac..45e0dd932c 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -168,6 +168,8 @@ static bool layer_height_ranges_equal(const t_layer_config_ranges &lr1, const t_ auto it2 = lr2.begin(); for (const auto &kvp1 : lr1) { const auto &kvp2 = *it2 ++; + if (!kvp2.second.has("layer_height") || !kvp1.second.has("layer_height")) + return false; if (std::abs(kvp1.first.first - kvp2.first.first ) > EPSILON || std::abs(kvp1.first.second - kvp2.first.second) > EPSILON || (check_layer_height && std::abs(kvp1.second.option("layer_height")->getFloat() - kvp2.second.option("layer_height")->getFloat()) > EPSILON)) diff --git a/src/libslic3r/PrintBase.cpp b/src/libslic3r/PrintBase.cpp index 76909647a1..8b8b481880 100644 --- a/src/libslic3r/PrintBase.cpp +++ b/src/libslic3r/PrintBase.cpp @@ -107,7 +107,7 @@ void PrintBase::set_status(int percent, const std::string &message, unsigned in if (m_status_callback) m_status_callback(SlicingStatus(percent, message, flags, warning_step)); else - BOOST_LOG_TRIVIAL(info) <sidetext = L("°C"); def->min = 0; - def->max = 300; + def->max = 120; def->set_default_value(new ConfigOptionInts{ 35 }); def = this->add("eng_plate_temp_initial_layer", coInts); @@ -822,7 +825,7 @@ void PrintConfigDef::init_fff_params() def = this->add("brim_type", coEnum); def->label = L("Brim type"); def->category = L("Support"); - def->tooltip = L("This controls the generation of the brim at outer side of models. " + def->tooltip = L("This controls the generation of the brim at outer and/or inner side of models. " "Auto means the brim width is analysed and calculated automatically."); def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); def->enum_values.emplace_back("auto_brim"); @@ -1668,8 +1671,10 @@ void PrintConfigDef::init_fff_params() def->enum_values.push_back("none"); def->enum_values.push_back("external"); def->enum_values.push_back("all"); + def->enum_values.push_back("allwalls"); def->enum_labels.push_back(L("None")); - def->enum_labels.push_back(L("Outer wall")); + def->enum_labels.push_back(L("Contour")); + def->enum_labels.push_back(L("Contour and hole")); def->enum_labels.push_back(L("All walls")); def->mode = comSimple; def->set_default_value(new ConfigOptionEnum(FuzzySkinType::None)); @@ -1680,6 +1685,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("The width within which to jitter. It's adversed to be below outer wall line width"); def->sidetext = L("mm"); def->min = 0; + def->max = 1; def->mode = comSimple; def->set_default_value(new ConfigOptionFloat(0.3)); @@ -1688,6 +1694,8 @@ void PrintConfigDef::init_fff_params() def->category = L("Others"); def->tooltip = L("The average diatance between the random points introducded on each line segment"); def->sidetext = L("mm"); + def->min = 0; + def->max = 5; def->mode = comSimple; def->set_default_value(new ConfigOptionFloat(0.8)); @@ -2839,7 +2847,7 @@ void PrintConfigDef::init_fff_params() def = this->add("support_filament", coInt); def->gui_type = ConfigOptionDef::GUIType::i_enum_open; - def->label = L("Support base"); + def->label = L("Support/raft base"); def->category = L("Support"); def->tooltip = L("Filament to print support base and raft. \"Default\" means no specific filament for support and current filament is used"); def->min = 0; @@ -2864,7 +2872,7 @@ void PrintConfigDef::init_fff_params() def = this->add("support_interface_filament", coInt); def->gui_type = ConfigOptionDef::GUIType::i_enum_open; - def->label = L("Support interface"); + def->label = L("Support/raft interface"); def->category = L("Support"); def->tooltip = L("Filament to print support interface. \"Default\" means no specific filament for support interface and current filament is used"); def->min = 0; @@ -2964,9 +2972,13 @@ void PrintConfigDef::init_fff_params() def->enum_values.push_back("auto"); def->enum_values.push_back("rectilinear"); def->enum_values.push_back("concentric"); + def->enum_values.push_back("rectilinear_interlaced"); + def->enum_values.push_back("grid"); def->enum_labels.push_back(L("Default")); def->enum_labels.push_back(L("Rectilinear")); def->enum_labels.push_back(L("Concentric")); + def->enum_labels.push_back(L("Rectilinear Interlaced")); + def->enum_labels.push_back(L("Grid")); def->mode = comAdvanced; def->set_default_value(new ConfigOptionEnum(smipRectilinear)); @@ -3024,7 +3036,8 @@ void PrintConfigDef::init_fff_params() def = this->add("independent_support_layer_height", coBool); def->label = L("Independent support layer height"); def->category = L("Support"); - def->tooltip = L("Support layer uses layer height independent with object layer. This is to support customizing z-gap and save print time."); + def->tooltip = L("Support layer uses layer height independent with object layer. This is to support customizing z-gap and save print time." + "This option will be invalid when the prime tower is enabled."); def->mode = comAdvanced; def->set_default_value(new ConfigOptionBool(true)); @@ -3238,7 +3251,8 @@ void PrintConfigDef::init_fff_params() def = this->add("enable_prime_tower", coBool); def->label = L("Enable"); - def->tooltip = L("Print a tower to prime material in nozzle after switching to a new material."); + def->tooltip = L("The wiping tower can be used to clean up the residue on the nozzle and stabilize the chamber pressure inside the nozzle, " + "in order to avoid appearance defects when printing objects."); def->mode = comSimple; def->set_default_value(new ConfigOptionBool(false)); @@ -4412,10 +4426,12 @@ void DynamicPrintConfig::normalize_fdm(int used_filaments) //if (alh_opt) // alh_opt->value = false; } + /* BBS: MusangKing - not sure if this is still valid, just comment it out cause "Independent support layer height" is re-opened. else { if (islh_opt) islh_opt->value = true; } + */ } } @@ -4501,6 +4517,7 @@ t_config_option_keys DynamicPrintConfig::normalize_fdm_2(int num_objects, int us // //alh_opt->value = false; //} } + /* BBS:MusangKing - use "global->support->Independent support layer height" widget to replace previous assignment else { if (islh_opt) { if (!islh_opt->value) { @@ -4510,6 +4527,7 @@ t_config_option_keys DynamicPrintConfig::normalize_fdm_2(int num_objects, int us //islh_opt->value = true; } } + */ } return changed_keys; @@ -4614,24 +4632,34 @@ std::string DynamicPrintConfig::get_filament_type(std::string &displayed_filamen if (is_support) { if (filament_id) { if (filament_id->get_at(id) == "GFS00") { - displayed_filament_type = "Support W"; + displayed_filament_type = "Sup.PLA"; return "PLA-S"; } else if (filament_id->get_at(id) == "GFS01") { - displayed_filament_type = "Support G"; + displayed_filament_type = "Sup.PA"; return "PA-S"; } else { - displayed_filament_type = filament_type->get_at(id); - return filament_type->get_at(id); + if (filament_type->get_at(id) == "PLA") { + displayed_filament_type = "Sup.PLA"; + return "PLA-S"; + } + else if (filament_type->get_at(id) == "PA") { + displayed_filament_type = "Sup.PA"; + return "PA-S"; + } + else { + displayed_filament_type = filament_type->get_at(id); + return filament_type->get_at(id); + } } } else { if (filament_type->get_at(id) == "PLA") { - displayed_filament_type = "Support W"; + displayed_filament_type = "Sup.PLA"; return "PLA-S"; } else if (filament_type->get_at(id) == "PA") { - displayed_filament_type = "Support G"; + displayed_filament_type = "Sup.PA"; return "PA-S"; } else { displayed_filament_type = filament_type->get_at(id); @@ -4975,6 +5003,33 @@ CLIActionsConfigDef::CLIActionsConfigDef() def->cli = "uptodate"; def->set_default_value(new ConfigOptionBool(false)); + def = this->add("mtcpp", coInt); + def->label = L("mtcpp"); + def->tooltip = L("max triangle count per plate for slicing."); + def->cli = "mtcpp"; + def->cli_params = "count"; + def->set_default_value(new ConfigOptionInt(1000000)); + + def = this->add("mstpp", coInt); + def->label = L("mstpp"); + def->tooltip = L("max slicing time per plate in seconds."); + def->cli = "mstpp"; + def->cli_params = "time"; + def->set_default_value(new ConfigOptionInt(300)); + + // must define new params here, otherwise comamnd param check will fail + def = this->add("no_check", coBool); + def->label = L("No check"); + def->tooltip = L("Do not run any validity checks, such as gcode path conflicts check."); + def->cli_params = "option"; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("normative_check", coBool); + def->label = L("Normative check"); + def->tooltip = L("Check the normative items."); + def->cli_params = "option"; + def->set_default_value(new ConfigOptionBool(true)); + /*def = this->add("help_fff", coBool); def->label = L("Help (FFF options)"); def->tooltip = L("Show the full list of print/G-code configuration options."); @@ -5000,7 +5055,7 @@ CLIActionsConfigDef::CLIActionsConfigDef() def->label = L("Send progress to pipe"); def->tooltip = L("Send progress to pipe."); def->cli_params = "pipename"; - def->set_default_value(new ConfigOptionString("cli_pipe")); + def->set_default_value(new ConfigOptionString("")); } //BBS: remove unused command currently @@ -5153,6 +5208,12 @@ CLIMiscConfigDef::CLIMiscConfigDef() def->cli_params = "\"filament1.json;filament2.json;...\""; def->set_default_value(new ConfigOptionStrings()); + def = this->add("skip_objects", coStrings); + def->label = L("Skip Objects"); + def->tooltip = L("Skip some objects in this print"); + def->cli_params = "\"3;5;10;77\""; + def->set_default_value(new ConfigOptionInts()); + /*def = this->add("output", coString); def->label = L("Output File"); def->tooltip = L("The file where the output will be written (if not specified, it will be based on the input file)."); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index eb1d84c4bf..a9be497202 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -40,6 +40,7 @@ enum class FuzzySkinType { None, External, All, + AllWalls, }; enum PrintHostType { @@ -104,7 +105,7 @@ enum SupportMaterialStyle { }; enum SupportMaterialInterfacePattern { - smipAuto, smipRectilinear, smipConcentric, + smipAuto, smipRectilinear, smipConcentric, smipRectilinearInterlaced, smipGrid }; // BBS @@ -117,7 +118,7 @@ inline bool is_tree(SupportType stype) }; inline bool is_tree_slim(SupportType type, SupportMaterialStyle style) { - return is_tree(type) && (style==smsDefault || style==smsTreeSlim); + return is_tree(type) && style==smsTreeSlim; }; inline bool is_auto(SupportType stype) { @@ -715,52 +716,46 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloat, bridge_angle)) ((ConfigOptionFloat, bridge_flow)) ((ConfigOptionFloat, bridge_speed)) - ((ConfigOptionPercent, bridge_density)) ((ConfigOptionBool, ensure_vertical_shell_thickness)) ((ConfigOptionEnum, top_surface_pattern)) - ((ConfigOptionFloat, top_solid_infill_flow_ratio)) - ((ConfigOptionFloat, bottom_solid_infill_flow_ratio)) ((ConfigOptionEnum, bottom_surface_pattern)) ((ConfigOptionFloat, outer_wall_line_width)) ((ConfigOptionFloat, outer_wall_speed)) - ((ConfigOptionFloatOrPercent, small_perimeter_speed)) - ((ConfigOptionFloat, small_perimeter_threshold)) ((ConfigOptionFloat, infill_direction)) ((ConfigOptionPercent, sparse_infill_density)) ((ConfigOptionEnum, sparse_infill_pattern)) ((ConfigOptionEnum, fuzzy_skin)) ((ConfigOptionFloat, fuzzy_skin_thickness)) ((ConfigOptionFloat, fuzzy_skin_point_distance)) - ((ConfigOptionFloat, filter_out_gap_fill)) ((ConfigOptionFloat, gap_infill_speed)) ((ConfigOptionInt, sparse_infill_filament)) ((ConfigOptionFloat, sparse_infill_line_width)) ((ConfigOptionPercent, infill_wall_overlap)) ((ConfigOptionFloat, sparse_infill_speed)) //BBS - ((ConfigOptionBool, infill_combination)) + ((ConfigOptionBool, infill_combination)) // Ironing options - ((ConfigOptionEnum, ironing_type)) - ((ConfigOptionPercent, ironing_flow)) - ((ConfigOptionFloat, ironing_spacing)) - ((ConfigOptionFloat, ironing_speed)) + ((ConfigOptionEnum, ironing_type)) + ((ConfigOptionPercent, ironing_flow)) + ((ConfigOptionFloat, ironing_spacing)) + ((ConfigOptionFloat, ironing_speed)) // Detect bridging perimeters - ((ConfigOptionBool, detect_overhang_wall)) - ((ConfigOptionInt, wall_filament)) - ((ConfigOptionFloat, inner_wall_line_width)) - ((ConfigOptionFloat, inner_wall_speed)) + ((ConfigOptionBool, detect_overhang_wall)) + ((ConfigOptionInt, wall_filament)) + ((ConfigOptionFloat, inner_wall_line_width)) + ((ConfigOptionFloat, inner_wall_speed)) // Total number of perimeters. - ((ConfigOptionInt, wall_loops)) - ((ConfigOptionFloat, minimum_sparse_infill_area)) - ((ConfigOptionInt, solid_infill_filament)) - ((ConfigOptionFloat, internal_solid_infill_line_width)) - ((ConfigOptionFloat, internal_solid_infill_speed)) + ((ConfigOptionInt, wall_loops)) + ((ConfigOptionFloat, minimum_sparse_infill_area)) + ((ConfigOptionInt, solid_infill_filament)) + ((ConfigOptionFloat, internal_solid_infill_line_width)) + ((ConfigOptionFloat, internal_solid_infill_speed)) // Detect thin walls. - ((ConfigOptionBool, detect_thin_wall)) - ((ConfigOptionFloat, top_surface_line_width)) - ((ConfigOptionInt, top_shell_layers)) - ((ConfigOptionFloat, top_shell_thickness)) - ((ConfigOptionFloat, top_surface_speed)) + ((ConfigOptionBool, detect_thin_wall)) + ((ConfigOptionFloat, top_surface_line_width)) + ((ConfigOptionInt, top_shell_layers)) + ((ConfigOptionFloat, top_shell_thickness)) + ((ConfigOptionFloat, top_surface_speed)) //BBS ((ConfigOptionBool, enable_overhang_speed)) ((ConfigOptionFloat, overhang_1_4_speed)) @@ -768,8 +763,9 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloat, overhang_3_4_speed)) ((ConfigOptionFloat, overhang_4_4_speed)) ((ConfigOptionBool, only_one_wall_top)) - ((ConfigOptionBool, only_one_wall_first_layer)) + //SoftFever + ((ConfigOptionBool, only_one_wall_first_layer)) ((ConfigOptionFloat, print_flow_ratio)) ((ConfigOptionFloatOrPercent, seam_gap)) ((ConfigOptionBool, role_based_wipe_speed)) @@ -778,6 +774,12 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionEnum, wall_infill_order)) ((ConfigOptionBool, precise_outer_wall)) ((ConfigOptionBool, overhang_speed_classic)) + ((ConfigOptionPercent, bridge_density)) + ((ConfigOptionFloat, filter_out_gap_fill)) + ((ConfigOptionFloatOrPercent, small_perimeter_speed)) + ((ConfigOptionFloat, small_perimeter_threshold)) + ((ConfigOptionFloat, top_solid_infill_flow_ratio)) + ((ConfigOptionFloat, bottom_solid_infill_flow_ratio)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 63ad41dd64..bbaa16a34f 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -458,19 +458,26 @@ void PrintObject::generate_support_material() this->_generate_support_material(); m_print->throw_if_canceled(); - } else { + } else if(!m_print->get_no_check_flag()) { // BBS: pop a warning if objects have significant amount of overhangs but support material is not enabled + m_print->set_status(50, L("Checking support necessity")); + typedef std::chrono::high_resolution_clock clock_; + typedef std::chrono::duration > second_; + std::chrono::time_point t0{ clock_::now() }; + SupportNecessaryType sntype = this->is_support_necessary(); + + double duration{ std::chrono::duration_cast(clock_::now() - t0).count() }; + BOOST_LOG_TRIVIAL(info) << std::fixed << std::setprecision(0) << "is_support_necessary takes " << duration << " secs."; + if (sntype != NoNeedSupp) { - m_print->set_status(50, L("Checking support necessity")); - if (sntype == SharpTail) { - std::string warning_message = format(L("It seems object %s has completely floating regions. Please re-orient the object or enable support generation."), - this->model_object()->name); - this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, warning_message, PrintStateBase::SlicingNeedSupportOn); - } else { - std::string warning_message = format(L("It seems object %s has large overhangs. Please enable support generation."), this->model_object()->name); - this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, warning_message, PrintStateBase::SlicingNeedSupportOn); - } + std::map reasons = { + {SharpTail,L("floating regions")}, + {Cantilever,L("floating cantilever")}, + {LargeOverhang,L("large overhangs")} }; + std::string warning_message = format(L("It seems object %s has %s. Please re-orient the object or enable support generation."), + this->model_object()->name, reasons[sntype]); + this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, warning_message, PrintStateBase::SlicingNeedSupportOn); } #if 0 @@ -2511,19 +2518,21 @@ void PrintObject::remove_bridges_from_contacts( if (surface.surface_type == stBottomBridge && surface.bridge_angle != -1) { auto bbox = get_extents(surface.expolygon); auto bbox_size = bbox.size(); - if (bbox_size[0] < max_bridge_length || bbox_size[1] < max_bridge_length) + if (bbox_size[0] < max_bridge_length && bbox_size[1] < max_bridge_length) polygons_append(bridges, surface.expolygon); else { if (break_bridge) { Polygons holes; - int x0 = bbox.min.x(); - int x1 = bbox.max.x(); - int y0 = bbox.min.y(); - int y1 = bbox.max.y(); + coord_t x0 = bbox.min.x(); + coord_t x1 = bbox.max.x(); + coord_t y0 = bbox.min.y(); + coord_t y1 = bbox.max.y(); const int grid_lw = int(w/2); // grid line width -#if 1 - if (fabs(surface.bridge_angle-0) fabs(bridge_direction(1))) + { // cut bridge along x-axis if bridge direction is aligned to x-axis more than to y-axis + // Note: surface.bridge_angle may be pi, so we can't compare it to 0 & pi/2. int step = bbox_size(0) / ceil(bbox_size(0) / max_bridge_length); for (int x = x0 + step; x < x1; x += step) { Polygon poly; @@ -2538,17 +2547,6 @@ void PrintObject::remove_bridges_from_contacts( holes.emplace_back(poly); } } -#else - int stepx = bbox_size(0) / ceil(bbox_size(0) / max_bridge_length); - int stepy = bbox_size(1) / ceil(bbox_size(1) / max_bridge_length); - for (int x = x0 + stepx; x < x1; x += stepx) - for (int y = y0 + stepy; y < y1; y += stepy) { - Polygon poly; - poly.points = {Point(x-grid_lw, y - grid_lw), Point(x+grid_lw, y - grid_lw), Point(x+grid_lw, y + grid_lw), Point(x-grid_lw, y + grid_lw)}; - holes.emplace_back(poly); - } - -#endif auto expoly = diff_ex(surface.expolygon, holes); polygons_append(bridges, expoly); } @@ -2586,9 +2584,9 @@ template void PrintObject::remove_bridges_from_contacts( SupportNecessaryType PrintObject::is_support_necessary() { -#if 0 static const double super_overhang_area_threshold = SQ(scale_(5.0)); - + const double cantilevel_dist_thresh = scale_(6); +#if 0 double threshold_rad = (m_config.support_threshold_angle.value < EPSILON ? 30 : m_config.support_threshold_angle.value + 1) * M_PI / 180.; int enforce_support_layers = m_config.enforce_support_layers; const coordf_t extrusion_width = m_config.line_width.value; @@ -2672,12 +2670,12 @@ SupportNecessaryType PrintObject::is_support_necessary() #else TreeSupport tree_support(*this, m_slicing_params); tree_support.support_type = SupportType::stTreeAuto; // need to set support type to fully utilize the power of feature detection - tree_support.detect_overhangs(); + tree_support.detect_overhangs(true); this->clear_support_layers(); if (tree_support.has_sharp_tails) return SharpTail; - else if (tree_support.has_cantilever) - return LargeOverhang; + else if (tree_support.has_cantilever && tree_support.max_cantilever_dist > cantilevel_dist_thresh) + return Cantilever; #endif return NoNeedSupp; } diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index 79ebd09790..d12cc3ff0e 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -345,11 +345,11 @@ static std::vector> slices_to_regions( if (!bbox_a.overlap(bbox_b)) continue; - if (intersection_ex(expoly_a, expoly_b).empty()) + ExPolygons temp = intersection_ex(expoly_b, expoly_a, ApplySafetyOffset::Yes); + if (temp.empty()) continue; - ExPolygons temp = intersection_ex(expoly_b, expoly_a); - if (expoly_a.area() > expoly_b.area()) + if (expoly_a.contour.length() > expoly_b.contour.length()) trimming_a.insert(trimming_a.end(), temp.begin(), temp.end()); else trimming_b.insert(trimming_b.end(), temp.begin(), temp.end()); @@ -397,6 +397,12 @@ static std::vector> slices_to_regions( // Clip every non-zero region preceding it. for (int idx_region2 = 0; idx_region2 < idx_region; ++ idx_region2) if (! temp_slices[idx_region2].expolygons.empty()) { + // Skip trim_overlap for now, because it slow down the performace so much for some special cases +#if 1 + if (const PrintObjectRegions::VolumeRegion& region2 = layer_range.volume_regions[idx_region2]; + !region2.model_volume->is_negative_volume() && overlap_in_xy(*region.bbox, *region2.bbox)) + temp_slices[idx_region2].expolygons = diff_ex(temp_slices[idx_region2].expolygons, temp_slices[idx_region].expolygons); +#else const PrintObjectRegions::VolumeRegion& region2 = layer_range.volume_regions[idx_region2]; if (!region2.model_volume->is_negative_volume() && overlap_in_xy(*region.bbox, *region2.bbox)) //BBS: handle negative_volume seperately, always minus the negative volume and don't need to trim overlap @@ -404,6 +410,7 @@ static std::vector> slices_to_regions( trim_overlap(temp_slices[idx_region2].expolygons, temp_slices[idx_region].expolygons); else temp_slices[idx_region2].expolygons = diff_ex(temp_slices[idx_region2].expolygons, temp_slices[idx_region].expolygons); +#endif } } } @@ -462,20 +469,33 @@ static std::vector> slices_to_regions( bool doesVolumeIntersect(VolumeSlices& vs1, VolumeSlices& vs2) { if (vs1.volume_id == vs2.volume_id) return true; + // two volumes in the same object should have same number of layers, otherwise the slicing is incorrect. if (vs1.slices.size() != vs2.slices.size()) return false; - for (int i = 0; i != vs1.slices.size(); ++i) { + auto& vs1s = vs1.slices; + auto& vs2s = vs2.slices; + bool is_intersect = false; - if (vs1.slices[i].empty()) continue; - if (!vs2.slices[i].empty() && !intersection_ex(vs1.slices[i], vs2.slices[i]).empty()) return true; - if (i + 1 != vs2.slices.size() && !vs2.slices[i + 1].empty()) { - if (!intersection_ex(vs1.slices[i], vs2.slices[i + 1]).empty()) return true; - } - if (i - 1 >= 0 && !vs2.slices[i - 1].empty()) { - if (!intersection_ex(vs1.slices[i], vs2.slices[i - 1]).empty()) return true; - } - } - return false; + tbb::parallel_for(tbb::blocked_range(0, vs1s.size()), + [&vs1s, &vs2s, &is_intersect](const tbb::blocked_range& range) { + for (auto i = range.begin(); i != range.end(); ++i) { + if (vs1s[i].empty()) continue; + + if (overlaps(vs1s[i], vs2s[i])) { + is_intersect = true; + break; + } + if (i + 1 != vs2s.size() && overlaps(vs1s[i], vs2s[i + 1])) { + is_intersect = true; + break; + } + if (i - 1 >= 0 && overlaps(vs1s[i], vs2s[i - 1])) { + is_intersect = true; + break; + } + } + }); + return is_intersect; } //BBS: grouping the volumes of an object according to their connection relationship @@ -581,15 +601,18 @@ void reGroupingLayerPolygons(std::vector& gvss, ExPolygons std::vector epsIndex; epsIndex.resize(eps.size(), -1); for (int ie = 0; ie != eps.size(); ie++) { + if (eps[ie].area() <= 0) + continue; + double minArea = eps[ie].area(); for (int iv = 0; iv != gvss.size(); iv++) { auto clipedExPolys = diff_ex(eps[ie], gvss[iv].slices); double area = 0; for (const auto& ce : clipedExPolys) { area += ce.area(); } - if (eps[ie].area() > 0 && area / eps[ie].area() < 0.3) { + if (area < minArea) { + minArea = area; epsIndex[ie] = iv; - break; } } } @@ -603,7 +626,7 @@ void reGroupingLayerPolygons(std::vector& gvss, ExPolygons } } -std::string fix_slicing_errors(PrintObject* object, LayerPtrs &layers, const std::function &throw_if_canceled) +std::string fix_slicing_errors(PrintObject* object, LayerPtrs &layers, const std::function &throw_if_canceled, int &firstLayerReplacedBy) { std::string error_msg;//BBS @@ -715,10 +738,14 @@ std::string fix_slicing_errors(PrintObject* object, LayerPtrs &layers, const std // BBS: first layer slices are sorted by volume group, if the first layer is empty and replaced by the 2nd layer // the later will be stored in "object->firstLayerObjGroupsMod()" - int firstLayerReplacedBy = 0; - if (!buggy_layers.empty() && buggy_layers.front() == 0) + if (!buggy_layers.empty() && buggy_layers.front() == 0 && layers.size() > 1) firstLayerReplacedBy = 1; + return error_msg; +} + +void groupingVolumesForBrim(PrintObject* object, LayerPtrs& layers, int firstLayerReplacedBy) +{ const auto scaled_resolution = scaled(object->print()->config().resolution.value); auto partsObjSliceByVolume = findPartVolumes(object->firstLayerObjSliceMod(), object->model_object()->volumes); groupingVolumes(partsObjSliceByVolume, object->firstLayerObjGroupsMod(), scaled_resolution, firstLayerReplacedBy); @@ -726,8 +753,6 @@ std::string fix_slicing_errors(PrintObject* object, LayerPtrs &layers, const std // BBS: the actual first layer slices stored in layers are re-sorted by volume group and will be used to generate brim reGroupingLayerPolygons(object->firstLayerObjGroupsMod(), layers.front()->lslices); - - return error_msg; } // Called by make_perimeters() @@ -752,15 +777,23 @@ void PrintObject::slice() m_layers = new_layers(this, generate_object_layers(m_slicing_params, layer_height_profile)); this->slice_volumes(); m_print->throw_if_canceled(); + int firstLayerReplacedBy = 0; + +#if 1 // Fix the model. //FIXME is this the right place to do? It is done repeateadly at the UI and now here at the backend. - std::string warning = fix_slicing_errors(this, m_layers, [this](){ m_print->throw_if_canceled(); }); + std::string warning = fix_slicing_errors(this, m_layers, [this](){ m_print->throw_if_canceled(); }, firstLayerReplacedBy); m_print->throw_if_canceled(); //BBS: send warning message to slicing callback if (!warning.empty()) { BOOST_LOG_TRIVIAL(info) << warning; this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, warning, PrintStateBase::SlicingReplaceInitEmptyLayers); } +#endif + + // BBS: the actual first layer slices stored in layers are re-sorted by volume group and will be used to generate brim + groupingVolumesForBrim(this, m_layers, firstLayerReplacedBy); + // Update bounding boxes, back up raw slices of complex models. tbb::parallel_for( tbb::blocked_range(0, m_layers.size()), diff --git a/src/libslic3r/ProjectTask.hpp b/src/libslic3r/ProjectTask.hpp index fbcdb91187..930fbbdc36 100644 --- a/src/libslic3r/ProjectTask.hpp +++ b/src/libslic3r/ProjectTask.hpp @@ -39,6 +39,7 @@ struct FilamentInfo std::string type; std::string color; std::string filament_id; + std::string brand; float used_m; float used_g; int tray_id; // start with 0 diff --git a/src/libslic3r/SLA/IndexedMesh.cpp b/src/libslic3r/SLA/IndexedMesh.cpp index 07c4203ab2..cd1c28278d 100644 --- a/src/libslic3r/SLA/IndexedMesh.cpp +++ b/src/libslic3r/SLA/IndexedMesh.cpp @@ -184,10 +184,11 @@ IndexedMesh::query_ray_hits(const Vec3d &s, const Vec3d &dir) const // Remove duplicates. They sometimes appear, for example when the ray is cast // along an axis of a cube due to floating-point approximations in igl (?) - hits.erase(std::unique(hits.begin(), hits.end(), - [](const igl::Hit& a, const igl::Hit& b) - { return a.t == b.t; }), - hits.end()); + // BBS: STUDIO-2591 A mesh with overlapping faces cannot be painted + //hits.erase(std::unique(hits.begin(), hits.end(), + // [](const igl::Hit& a, const igl::Hit& b) + // { return a.t == b.t; }), + // hits.end()); // Convert the igl::Hit into hit_result outs.reserve(hits.size()); diff --git a/src/libslic3r/SLA/Pad.cpp b/src/libslic3r/SLA/Pad.cpp index 1e5de51589..447a416341 100644 --- a/src/libslic3r/SLA/Pad.cpp +++ b/src/libslic3r/SLA/Pad.cpp @@ -204,7 +204,7 @@ public: void add(const ExPolygon &ep) { m_polys.emplace_back(ep); - m_index.insert(BoundingBox{ep}, unsigned(m_index.size())); + m_index.insert(get_extents(ep), unsigned(m_index.size())); } // Check an arbitrary polygon for intersection with the indexed polygons diff --git a/src/libslic3r/SLA/SupportPointGenerator.cpp b/src/libslic3r/SLA/SupportPointGenerator.cpp index c32da04319..c7a1b65c6b 100644 --- a/src/libslic3r/SLA/SupportPointGenerator.cpp +++ b/src/libslic3r/SLA/SupportPointGenerator.cpp @@ -4,6 +4,7 @@ #include #include "SupportPointGenerator.hpp" +#include "Geometry/ConvexHull.hpp" #include "Concurrency.hpp" #include "Model.hpp" #include "ExPolygon.hpp" @@ -11,7 +12,6 @@ #include "Point.hpp" #include "ClipperUtils.hpp" #include "Tesselate.hpp" -#include "ExPolygonCollection.hpp" #include "MinAreaBoundingBox.hpp" #include "libslic3r.h" @@ -550,7 +550,7 @@ void SupportPointGenerator::uniformly_cover(const ExPolygons& islands, Structure // auto bb = get_extents(islands); if (flags & icfIsNew) { - auto chull = ExPolygonCollection{islands}.convex_hull(); + auto chull = Geometry::convex_hull(islands); auto rotbox = MinAreaBoundigBox{chull, MinAreaBoundigBox::pcConvex}; Vec2d bbdim = {unscaled(rotbox.width()), unscaled(rotbox.height())}; diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 7dae5072ed..ba6fb7032e 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -335,6 +335,7 @@ remove_unconnected_vertices(const indexed_triangle_set &its) // Drill holes into the hollowed/original mesh. void SLAPrint::Steps::drill_holes(SLAPrintObject &po) { + /* bool needs_drilling = ! po.m_model_object->sla_drain_holes.empty(); bool is_hollowed = (po.m_hollowing_data && po.m_hollowing_data->interior && @@ -465,6 +466,7 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) if (hole_fail) po.active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL, L("Failed to drill some holes into the model")); + */ } // The slicing will be performed on an imaginary 1D grid which starts from diff --git a/src/libslic3r/SVG.cpp b/src/libslic3r/SVG.cpp index 4ad31bb332..c951380f63 100644 --- a/src/libslic3r/SVG.cpp +++ b/src/libslic3r/SVG.cpp @@ -88,10 +88,8 @@ void SVG::draw(const ExPolygon &expolygon, std::string fill, const float fill_op this->fill = fill; std::string d; - Polygons pp = expolygon; - for (Polygons::const_iterator p = pp.begin(); p != pp.end(); ++p) { - d += this->get_path_d(*p, true) + " "; - } + for (const Polygon &p : to_polygons(expolygon)) + d += this->get_path_d(p, true) + " "; this->path(d, true, 0, fill_opacity); } @@ -279,13 +277,14 @@ std::string SVG::get_path_d(const ClipperLib::Path &path, double scale, bool clo return d.str(); } +// font_size: font-size={font_size*10}px void SVG::draw_text(const Point &pt, const char *text, const char *color, int font_size) { fprintf(this->f, - "%s", + "%s", to_svg_x(pt(0)-origin(0)), to_svg_y(pt(1)-origin(1)), - color, text); + font_size*10, color, text); } void SVG::draw_legend(const Point &pt, const char *text, const char *color) @@ -391,7 +390,7 @@ void SVG::export_expolygons(const char *path, const std::vector 0) for (const ExPolygon &expoly : exp_with_attr.first) - svg.draw((Points)expoly, exp_with_attr.second.color_points, exp_with_attr.second.radius_points); + svg.draw(to_points(expoly), exp_with_attr.second.color_points, exp_with_attr.second.radius_points); // Export legend. // 1st row diff --git a/src/libslic3r/Shape/TextShape.cpp b/src/libslic3r/Shape/TextShape.cpp index d13bcca98b..e740905ce5 100644 --- a/src/libslic3r/Shape/TextShape.cpp +++ b/src/libslic3r/Shape/TextShape.cpp @@ -95,7 +95,7 @@ std::vector init_occt_fonts() return stdFontNames; } -static bool TextToBRep(const char* text, const char* font, const float theTextHeight, Font_FontAspect& theFontAspect, TopoDS_Shape& theShape) +static bool TextToBRep(const char* text, const char* font, const float theTextHeight, Font_FontAspect& theFontAspect, TopoDS_Shape& theShape, double& text_width) { Standard_Integer anArgIt = 1; Standard_CString aName = "text_shape"; @@ -122,8 +122,24 @@ static bool TextToBRep(const char* text, const char* font, const float theTextHe aPenAx3 = gp_Ax3(aPenLoc, aNormal, aDirection); + Handle(Font_TextFormatter) aFormatter = new Font_TextFormatter(); + aFormatter->Reset(); + aFormatter->SetupAlignment(aHJustification, aVJustification); + aFormatter->Append(aText, *aFont.FTFont()); + aFormatter->Format(); + + // get the text width + text_width = 0; + NCollection_String coll_str = aText; + for (NCollection_Utf8Iter anIter = coll_str.Iterator(); *anIter != 0;) { + const Standard_Utf32Char aCharThis = *anIter; + const Standard_Utf32Char aCharNext = *++anIter; + double width = aFont.AdvanceX(aCharThis, aCharNext); + text_width += width; + } + Font_BRepTextBuilder aBuilder; - theShape = aBuilder.Perform(aFont, aText, aPenAx3, aHJustification, aVJustification); + theShape = aBuilder.Perform(aFont, aFormatter, aPenAx3); return true; } @@ -221,7 +237,7 @@ static void MakeMesh(TopoDS_Shape& theSolid, TriangleMesh& theMesh) theMesh.from_stl(stl); } -void load_text_shape(const char*text, const char* font, const float text_height, const float thickness, bool is_bold, bool is_italic, TriangleMesh& text_mesh) +void load_text_shape(const char*text, const char* font, const float text_height, const float thickness, bool is_bold, bool is_italic, TextResult &text_result) { Handle(Font_FontMgr) aFontMgr = Font_FontMgr::GetInstance(); if (aFontMgr->GetAvailableFonts().IsEmpty()) @@ -238,14 +254,14 @@ void load_text_shape(const char*text, const char* font, const float text_height, else aFontAspect = Font_FontAspect_Regular; - if (!TextToBRep(text, font, text_height, aFontAspect, aTextBase)) + if (!TextToBRep(text, font, text_height, aFontAspect, aTextBase, text_result.text_width)) return; TopoDS_Shape aTextShape; if (!Prism(aTextBase, thickness, aTextShape)) return; - MakeMesh(aTextShape, text_mesh); + MakeMesh(aTextShape, text_result.text_mesh); } }; // namespace Slic3r diff --git a/src/libslic3r/Shape/TextShape.hpp b/src/libslic3r/Shape/TextShape.hpp index b0817d7fc4..75c93bc641 100644 --- a/src/libslic3r/Shape/TextShape.hpp +++ b/src/libslic3r/Shape/TextShape.hpp @@ -4,8 +4,14 @@ namespace Slic3r { class TriangleMesh; +struct TextResult +{ + TriangleMesh text_mesh; + double text_width; +}; + extern std::vector init_occt_fonts(); -extern void load_text_shape(const char* text, const char* font, const float text_height, const float thickness, bool is_bold, bool is_italic, TriangleMesh& text_mesh); +extern void load_text_shape(const char *text, const char *font, const float text_height, const float thickness, bool is_bold, bool is_italic, TextResult &text_result); std::map get_occt_fonts_maps(); diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index dc86831d64..38b3a9d514 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -31,9 +31,9 @@ #endif // SUPPORT_USE_AGG_RASTERIZER // #define SLIC3R_DEBUG - +// #define SUPPORT_TREE_DEBUG_TO_SVG // Make assert active if SLIC3R_DEBUG -#ifdef SLIC3R_DEBUG +#if defined(SLIC3R_DEBUG) || defined(SUPPORT_TREE_DEBUG_TO_SVG) #define DEBUG #define _DEBUG #undef NDEBUG @@ -412,7 +412,12 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object support_pattern == smpHoneycomb ? ipHoneycomb : m_support_params.support_density > 0.95 || m_support_params.with_sheath ? ipRectilinear : ipSupportBase; m_support_params.interface_fill_pattern = (m_support_params.interface_density > 0.95 ? ipRectilinear : ipSupportBase); - m_support_params.contact_fill_pattern = + if (m_object_config->support_interface_pattern == smipGrid) + m_support_params.contact_fill_pattern = ipGrid; + else if (m_object_config->support_interface_pattern == smipRectilinearInterlaced) + m_support_params.contact_fill_pattern = ipRectilinear; + else + m_support_params.contact_fill_pattern = (m_object_config->support_interface_pattern == smipAuto && m_slicing_params.soluble_interface) || m_object_config->support_interface_pattern == smipConcentric ? ipConcentric : @@ -609,6 +614,29 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) layers_append(layers_sorted, base_interface_layers); // Sort the layers lexicographically by a raising print_z and a decreasing height. std::sort(layers_sorted.begin(), layers_sorted.end(), [](auto *l1, auto *l2) { return *l1 < *l2; }); + + // BBS: MusangKing - erase mini layer heights (< 0.08mm) arised by top/bottom_z_distance & top_contacts under variable layer height + if (this->synchronize_layers() && !object.slicing_parameters().soluble_interface) { + auto thres = m_support_params.support_layer_height_min - EPSILON; + for (size_t i = 1; i < layers_sorted.size() - 1; ++i) { + auto& lowr = layers_sorted[i - 1]; + auto& curr = layers_sorted[i]; + auto& higr = layers_sorted[i + 1]; + // "Rounding" suspicious top/bottom contacts + if (curr->layer_type == sltTopContact || curr->layer_type == sltBottomContact) { + // Check adjacent-layer print_z diffs + coordf_t height_low = curr->print_z - lowr->print_z; + coordf_t height_high = higr->print_z - curr->print_z; + if (height_low < thres || height_high < thres) { + // Mark to-be-deleted layer as Unknown type + curr->layer_type = sltUnknown; + } + } + } + // Retains the order + layers_sorted.erase(std::remove_if(layers_sorted.begin(), layers_sorted.end(), [](MyLayer* l) {return l->layer_type == sltUnknown; }), layers_sorted.end()); + } + int layer_id = 0; int layer_id_interface = 0; assert(object.support_layers().empty()); @@ -692,6 +720,18 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) } #endif /* SLIC3R_DEBUG */ +#if 0 // #ifdef SLIC3R_DEBUG + // check bounds + std::ofstream out; + out.open("./SVG/ns_support_layers.txt"); + if (out.is_open()) { + out << "### Support Layers ###" << std::endl; + for (auto& i : object.support_layers()) { + out << i->print_z << std::endl; + } + } +#endif /* SLIC3R_DEBUG */ + // Generate the actual toolpaths and save them into each layer. this->generate_toolpaths(object.support_layers(), raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers); @@ -956,16 +996,18 @@ public: if (!support_polygons_simplified.empty()) bbox.merge(get_extents(support_polygons_simplified)); SVG svg(debug_out_path("extract_support_from_grid_trimmed-%s-%d-%d-%lf.svg", step_name, iRun, layer_id, print_z).c_str(), bbox); - svg.draw(union_ex(support_polygons_simplified), "gray", 0.25f); - svg.draw(islands, "red", 0.5f); - svg.draw(union_ex(out), "green", 0.5f); - svg.draw(union_ex(*m_support_polygons), "blue", 0.5f); - svg.draw_outline(islands, "red", "red", scale_(0.05)); - svg.draw_outline(union_ex(out), "green", "green", scale_(0.05)); - svg.draw_outline(union_ex(*m_support_polygons), "blue", "blue", scale_(0.05)); - for (const Point &pt : samples) - svg.draw(pt, "black", coord_t(scale_(0.15))); - svg.Close(); + if (svg.is_opened()) { + svg.draw(union_ex(support_polygons_simplified), "gray", 0.25f); + svg.draw(islands, "red", 0.5f); + svg.draw(union_ex(out), "green", 0.5f); + svg.draw(union_ex(*m_support_polygons), "blue", 0.5f); + svg.draw_outline(islands, "red", "red", scale_(0.05)); + svg.draw_outline(union_ex(out), "green", "green", scale_(0.05)); + svg.draw_outline(union_ex(*m_support_polygons), "blue", "blue", scale_(0.05)); + for (const Point& pt : samples) + svg.draw(pt, "black", coord_t(scale_(0.15))); + svg.Close(); + } #endif /* SLIC3R_DEBUG */ if (m_support_angle != 0.) @@ -1486,14 +1528,13 @@ static const double sharp_tail_max_support_height = 16.f; // Tuple: overhang_polygons, contact_polygons, enforcer_polygons, no_interface_offset // no_interface_offset: minimum of external perimeter widths -static inline Polygons detect_overhangs( +static inline ExPolygons detect_overhangs( const Layer &layer, const size_t layer_id, Polygons &lower_layer_polygons, const PrintConfig &print_config, const PrintObjectConfig &object_config, SupportAnnotations &annotations, - SlicesMarginCache &slices_margin, const double gap_xy #ifdef SLIC3R_DEBUG , size_t iRun @@ -1508,9 +1549,10 @@ static inline Polygons detect_overhangs( const bool buildplate_only = ! annotations.buildplate_covered.empty(); // If user specified a custom angle threshold, convert it to radians. // Zero means automatic overhang detection. - const double threshold_rad = (object_config.support_threshold_angle.value > 0) ? - M_PI * double(object_config.support_threshold_angle.value + 1) / 180. : // +1 makes the threshold inclusive - 0.; + // +1 makes the threshold inclusive + double thresh_angle = object_config.support_threshold_angle.value > 0 ? object_config.support_threshold_angle.value + 1 : 0; + thresh_angle = std::min(thresh_angle, 89.); // BBS should be smaller than 90 + const double threshold_rad = Geometry::deg2rad(thresh_angle); const coordf_t max_bridge_length = scale_(object_config.max_bridge_length.value); const bool bridge_no_support = object_config.bridge_no_support.value; const coordf_t xy_expansion = scale_(object_config.support_expansion.value); @@ -1535,9 +1577,16 @@ static inline Polygons detect_overhangs( // Generate overhang / contact_polygons for non-raft layers. const Layer &lower_layer = *layer.lower_layer; const bool has_enforcer = !annotations.enforcers_layers.empty() && !annotations.enforcers_layers[layer_id].empty(); - const ExPolygons& lower_layer_expolys = lower_layer.lslices; - const ExPolygons& lower_layer_sharptails = lower_layer.sharp_tails; - auto& lower_layer_sharptails_height = lower_layer.sharp_tails_height; + // Can't directly use lower_layer.lslices, or we'll miss some very sharp tails. + // Filter out areas whose diameter that is smaller than extrusion_width. Do not use offset2() for this purpose! + // FIXME if there are multiple regions with different extrusion width, the following code may not be right. + float fw = float(layer.regions().front()->flow(frExternalPerimeter).scaled_width()); + ExPolygons lower_layer_expolys; + for (const ExPolygon& expoly : lower_layer.lslices) { + if (!offset_ex(expoly, -fw / 2).empty()) { + lower_layer_expolys.emplace_back(expoly); + } + } float lower_layer_offset = 0; for (LayerRegion *layerm : layer.regions()) { @@ -1588,73 +1637,13 @@ static inline Polygons detect_overhangs( for (ExPolygon& expoly : layerm->raw_slices) { bool is_sharp_tail = false; float accum_height = layer.height; - do { - if (!g_config_support_sharp_tails) { - is_sharp_tail = false; - break; - } - // 1. nothing below - // Check whether this is a sharp tail region. - // Should use lower_layer_expolys without any offset. Otherwise, it may missing sharp tails near the main body. - if (intersection_ex({ expoly }, lower_layer_expolys).empty()) { - is_sharp_tail = expoly.area() < area_thresh_well_supported && !offset_ex(expoly,-0.5*fw).empty(); - break; - } - - // 2. something below - // check whether this is above a sharp tail region. - - // 2.1 If no sharp tail below, this is considered as common region. - ExPolygons supported_by_lower = intersection_ex({ expoly }, lower_layer_sharptails); - if (supported_by_lower.empty()) { - is_sharp_tail = false; - break; - } - - // 2.2 If sharp tail below, check whether it support this region enough. - float supported_area = 0.f; - BoundingBox bbox; - for (ExPolygon& temp : supported_by_lower) { - supported_area += temp.area(); - bbox.merge(get_extents(temp)); - } -#if 0 - if (supported_area > area_thresh_well_supported) { - is_sharp_tail = false; - break; - } -#endif - if (bbox.size().x() > length_thresh_well_supported && bbox.size().y() > length_thresh_well_supported) { - is_sharp_tail = false; - break; - } - - // 2.3 check whether sharp tail exceed the max height - for (auto& lower_sharp_tail_height : lower_layer_sharptails_height) { - if (!intersection_ex(*lower_sharp_tail_height.first, expoly).empty()) { - accum_height += lower_sharp_tail_height.second; - break; - } - } - - if (accum_height >= sharp_tail_max_support_height) { - is_sharp_tail = false; - break; - } - - // 2.4 if the area grows fast than threshold, it get connected to other part or - // it has a sharp slop and will be auto supported. - ExPolygons new_overhang_expolys = diff_ex({ expoly }, lower_layer_sharptails); - Point size_diff = get_extents(new_overhang_expolys).size() - get_extents(lower_layer_sharptails).size(); - if (size_diff.both_comp(Point(scale_(5),scale_(5)),">") || !offset_ex(new_overhang_expolys, -5.0 * fw).empty()) { - is_sharp_tail = false; - break; - } - - // 2.5 mark the expoly as sharptail - is_sharp_tail = true; - } while (0); + // 1. nothing below + // Check whether this is a sharp tail region. + // Should use lower_layer_expolys without any offset. Otherwise, it may missing sharp tails near the main body. + if (g_config_support_sharp_tails && !overlaps(offset_ex(expoly, 0.5 * fw), lower_layer_expolys)) { + is_sharp_tail = expoly.area() < area_thresh_well_supported && !offset_ex(expoly,-0.1*fw).empty(); + } if (is_sharp_tail) { ExPolygons overhang = diff_ex({ expoly }, lower_layer_polygons); @@ -1663,7 +1652,7 @@ static inline Polygons detect_overhangs( overhang = offset_ex(overhang, 0.05 * fw); polygons_append(diff_polygons, to_polygons(overhang)); } - } + } } if (diff_polygons.empty()) @@ -1692,8 +1681,30 @@ static inline Polygons detect_overhangs( } // for each layer.region } - // BBS: hotfix to make sure ccw polygon is before cw polygon - return to_polygons(union_ex(overhang_polygons)); + ExPolygons overhang_areas = union_ex(overhang_polygons); + // check cantilever + if (layer.lower_layer) { + for (ExPolygon& poly : overhang_areas) { + float fw = float(layer.regions().front()->flow(frExternalPerimeter).scaled_width()); + auto cluster_boundary_ex = intersection_ex(poly, offset_ex(layer.lower_layer->lslices, scale_(0.5))); + Polygons cluster_boundary = to_polygons(cluster_boundary_ex); + if (cluster_boundary.empty()) continue; + double dist_max = 0; + for (auto& pt : poly.contour.points) { + double dist_pt = std::numeric_limits::max(); + for (auto& ply : cluster_boundary) { + double d = ply.distance_to(pt); + dist_pt = std::min(dist_pt, d); + } + dist_max = std::max(dist_max, dist_pt); + } + if (dist_max > scale_(3)) { // is cantilever if the farmost point is larger than 3mm away from base + layer.cantilevers.emplace_back(poly); + } + } + } + + return overhang_areas; } // Tuple: overhang_polygons, contact_polygons, enforcer_polygons, no_interface_offset @@ -1721,11 +1732,6 @@ static inline std::tuple detect_contacts( // BBS. const bool auto_normal_support = object_config.support_type.value == stNormalAuto; const bool buildplate_only = !annotations.buildplate_covered.empty(); - // If user specified a custom angle threshold, convert it to radians. - // Zero means automatic overhang detection. - const double threshold_rad = (object_config.support_threshold_angle.value > 0) ? - M_PI * double(object_config.support_threshold_angle.value + 1) / 180. : // +1 makes the threshold inclusive - 0.; float no_interface_offset = 0.f; if (layer_id == 0) @@ -1851,7 +1857,7 @@ static inline std::pairlower_layer->print_z; } else { print_z = layer.bottom_z() - slicing_params.gap_support_object; - height = print_config.independent_support_layer_height ? 0. : object_config.layer_height; + height = print_config.independent_support_layer_height ? 0. : layer.lower_layer->height/*object_config.layer_height*/; // BBS: need to consider adaptive layer heights bottom_z = print_z - height; // Ignore this contact area if it's too low. // Don't want to print a layer below the first layer height as it may not stick well. @@ -2117,27 +2123,30 @@ static void merge_contact_layers(const SlicingParameters &slicing_params, double struct OverhangCluster { - std::map> layer_overhangs; - Polygons merged_overhangs_dilated; + std::map> layer_overhangs; + ExPolygons merged_overhangs_dilated; int min_layer = 1e7; int max_layer = 0; coordf_t offset_scaled = 0; + bool is_cantilever = false; + bool is_sharp_tail = false; + bool is_small_overhang = false; - OverhangCluster(Polygon* overhang, int layer_nr, coordf_t offset_scaled) { + OverhangCluster(ExPolygon* overhang, int layer_nr, coordf_t offset_scaled) { this->offset_scaled = offset_scaled; insert(overhang, layer_nr); } - void insert(Polygon* overhang_new, int layer_nr) { + void insert(ExPolygon* overhang_new, int layer_nr) { if (layer_overhangs.find(layer_nr) != layer_overhangs.end()) { layer_overhangs[layer_nr].push_back(overhang_new); } else { - layer_overhangs.emplace(layer_nr, std::vector{ overhang_new }); + layer_overhangs.emplace(layer_nr, std::vector{ overhang_new }); } - Polygons overhang_dilated = offset_scaled > EPSILON ? expand(*overhang_new, offset_scaled) : Polygons{ *overhang_new }; + ExPolygons overhang_dilated = offset_scaled > EPSILON ? offset_ex(*overhang_new, offset_scaled) : ExPolygons{ *overhang_new }; if (!overhang_dilated.empty()) - merged_overhangs_dilated = union_(merged_overhangs_dilated, overhang_dilated); + merged_overhangs_dilated = union_ex(merged_overhangs_dilated, overhang_dilated); min_layer = std::min(min_layer, layer_nr); max_layer = std::max(max_layer, layer_nr); } @@ -2146,36 +2155,39 @@ struct OverhangCluster { return max_layer - min_layer + 1; } - bool intersects(const Polygon& overhang_new, int layer_nr) { + bool intersects(const ExPolygon& overhang_new, int layer_nr) { if (layer_nr < 1) return false; - auto it = layer_overhangs.find(layer_nr - 1); - if (it == layer_overhangs.end()) + //auto it = layer_overhangs.find(layer_nr - 1); + //if (it == layer_overhangs.end()) + // return false; + //ExPolygons overhangs_lower; + //for (ExPolygon* poly : it->second) { + // overhangs_lower.push_back(*poly); + //} + if (layer_nrmax_layer + 1) return false; - - Polygons overhangs_lower; - for (Polygon* poly : it->second) { - overhangs_lower.push_back(*poly); - } - const Polygons overhang_dilated = expand(overhang_new, offset_scaled); - return !intersection(overhang_dilated, overhangs_lower).empty(); + const ExPolygons overhang_dilated = offset_ex(overhang_new, offset_scaled); + return overlaps(overhang_dilated, merged_overhangs_dilated); } }; -static void add_overhang(std::vector& clusters, Polygon* overhang, int layer_nr, coordf_t offset_scaled) { +static OverhangCluster* add_overhang(std::vector& clusters, ExPolygon* overhang, int layer_nr, coordf_t offset_scaled) { + OverhangCluster* cluster = nullptr; bool found = false; for (int i = 0; i < clusters.size(); i++) { - auto& cluster = clusters[i]; - if (cluster.intersects(*overhang, layer_nr)) { - cluster.insert(overhang, layer_nr); - found = true; + auto cluster_i = &clusters[i]; + if (cluster_i->intersects(*overhang, layer_nr)) { + cluster_i->insert(overhang, layer_nr); + cluster = cluster_i; break; } } - if (!found) { - clusters.emplace_back(overhang, layer_nr, offset_scaled); + if (!cluster) { + cluster = &clusters.emplace_back(overhang, layer_nr, offset_scaled); } + return cluster; }; // Generate top contact layers supporting overhangs. @@ -2214,113 +2226,186 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ contact_out.assign(num_layers * 2, nullptr); tbb::spin_mutex layer_storage_mutex; - std::vector overhangs_per_layers(num_layers); - for (size_t layer_id = this->has_raft() ? 0 : 1; layer_id < num_layers; layer_id++) { - const Layer& layer = *object.layers()[layer_id]; - Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers()[layer_id - 1]->lslices); - SlicesMarginCache slices_margin; + std::vector overhangs_per_layers(num_layers); + size_t layer_id_start = this->has_raft() ? 0 : 1; + // main part of overhang detection can be parallel + tbb::parallel_for(tbb::blocked_range(layer_id_start, num_layers), + [&](const tbb::blocked_range& range) { + for (size_t layer_id = range.begin(); layer_id < range.end(); layer_id++) { + const Layer& layer = *object.layers()[layer_id]; + Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers()[layer_id - 1]->lslices); - Polygons overhang_polygons = detect_overhangs(layer, layer_id, lower_layer_polygons, *m_print_config, *m_object_config, annotations, slices_margin, m_support_params.gap_xy + overhangs_per_layers[layer_id] = detect_overhangs(layer, layer_id, lower_layer_polygons, *m_print_config, *m_object_config, annotations, m_support_params.gap_xy #ifdef SLIC3R_DEBUG - , iRun + , iRun #endif // SLIC3R_DEBUG - ); + ); - overhangs_per_layers[layer_id] = std::move(overhang_polygons); + if (object.print()->canceled()) + break; + } + } + ); // end tbb::parallel_for - if (object.print()->canceled()) - return MyLayersPtr(); + if (object.print()->canceled()) + return MyLayersPtr(); + + // check if the sharp tails should be extended higher + bool detect_first_sharp_tail_only = false; + const coordf_t extrusion_width = m_object_config->line_width.value; + const coordf_t extrusion_width_scaled = scale_(extrusion_width); + if (is_auto(m_object_config->support_type.value) && g_config_support_sharp_tails && !detect_first_sharp_tail_only) { + for (size_t layer_nr = 0; layer_nr < object.layer_count(); layer_nr++) { + if (object.print()->canceled()) + break; + + const Layer* layer = object.get_layer(layer_nr); + const Layer* lower_layer = layer->lower_layer; + // skip if: + // 1) if the current layer is already detected as sharp tails + // 2) lower layer has no sharp tails + if (!lower_layer || layer->sharp_tails.empty() == false || lower_layer->sharp_tails.empty() == true) + continue; + + // BBS detect sharp tail + const ExPolygons& lower_layer_sharptails = lower_layer->sharp_tails; + auto& lower_layer_sharptails_height = lower_layer->sharp_tails_height; + for (const ExPolygon& expoly : layer->lslices) { + bool is_sharp_tail = false; + float accum_height = layer->height; + do { + // 2. something below + // check whether this is above a sharp tail region. + + // 2.1 If no sharp tail below, this is considered as common region. + ExPolygons supported_by_lower = intersection_ex({ expoly }, lower_layer_sharptails); + if (supported_by_lower.empty()) { + is_sharp_tail = false; + break; + } + + // 2.2 If sharp tail below, check whether it support this region enough. +#if 0 + // judge by area isn't reliable, failure cases include 45 degree rotated cube + float supported_area = area(supported_by_lower); + if (supported_area > area_thresh_well_supported) { + is_sharp_tail = false; + break; + } +#endif + BoundingBox bbox = get_extents(supported_by_lower); + if (bbox.size().x() > length_thresh_well_supported && bbox.size().y() > length_thresh_well_supported) { + is_sharp_tail = false; + break; + } + + // 2.3 check whether sharp tail exceed the max height + for (auto& lower_sharp_tail_height : lower_layer_sharptails_height) { + if (lower_sharp_tail_height.first->overlaps(expoly)) { + accum_height += lower_sharp_tail_height.second; + break; + } + } + if (accum_height >= sharp_tail_max_support_height) { + is_sharp_tail = false; + break; + } + + // 2.4 if the area grows fast than threshold, it get connected to other part or + // it has a sharp slop and will be auto supported. + ExPolygons new_overhang_expolys = diff_ex({ expoly }, lower_layer_sharptails); + Point size_diff = get_extents(new_overhang_expolys).size() - get_extents(lower_layer_sharptails).size(); + if (size_diff.both_comp(Point(scale_(5), scale_(5)), ">") || !offset_ex(new_overhang_expolys, -5.0 * extrusion_width_scaled).empty()) { + is_sharp_tail = false; + break; + } + + // 2.5 mark the expoly as sharptail + is_sharp_tail = true; + } while (0); + + if (is_sharp_tail) { + ExPolygons overhang = diff_ex({ expoly }, lower_layer->lslices); + layer->sharp_tails.push_back(expoly); + layer->sharp_tails_height.insert({ &expoly, accum_height }); + append(overhangs_per_layers[layer_nr], overhang); +#ifdef SUPPORT_TREE_DEBUG_TO_SVG + SVG svg(get_svg_filename(std::to_string(layer->print_z), "sharp_tail"), object.bounding_box()); + if (svg.is_opened()) svg.draw(overhang, "yellow"); +#endif + } + + } + } } if (object.print()->canceled()) return MyLayersPtr(); - // BBS + // BBS group overhang clusters if (g_config_remove_small_overhangs) { std::vector clusters; double fw_scaled = scale_(m_object_config->line_width); - std::set removed_overhang; + std::set removed_overhang; - for (size_t layer_id = this->has_raft() ? 0 : 1; layer_id < num_layers; layer_id++) { - for (Polygon& overhang : overhangs_per_layers[layer_id]) { - if (overhang.is_counter_clockwise()) - add_overhang(clusters, &overhang, layer_id, fw_scaled); + for (size_t layer_id = layer_id_start; layer_id < num_layers; layer_id++) { + const Layer* layer = object.get_layer(layer_id); + for (auto& overhang : overhangs_per_layers[layer_id]) { + OverhangCluster* cluster = add_overhang(clusters, &overhang, layer_id, fw_scaled); + if (overlaps({ overhang }, layer->cantilevers)) + cluster->is_cantilever = true; } } for (OverhangCluster& cluster : clusters) { - // 1. check overhang span size is smaller than 3mm - //auto bbox_size = get_extents(cluster.merged_overhangs_dilated).size(); - //const double dimension_limit = scale_(3.0) + 2 * fw_scaled; - //if (bbox_size.x() > dimension_limit || bbox_size.y() > dimension_limit) - // continue; - - double area = 0.f; - // 2. check overhang cluster size is smaller than 3.0 * fw_scaled - auto erode1 = offset(cluster.merged_overhangs_dilated, -2.5 * fw_scaled); - for (Polygon& poly : erode1) - area += poly.area() * (poly.is_counter_clockwise() ? 1.0 : -1.0); - if (std::abs(area) > SQ(scale_(0.1))) - continue; - // 3. check whether the small overhang is sharp tail - bool is_sharp_tail = false; - for (size_t layer_id = cluster.min_layer; layer_id < cluster.max_layer; layer_id++) { - const Layer& layer = *object.layers()[layer_id]; - if (!intersection_ex(layer.sharp_tails, cluster.merged_overhangs_dilated).empty()) { - is_sharp_tail = true; + cluster.is_sharp_tail = false; + for (size_t layer_id = cluster.min_layer; layer_id <= cluster.max_layer; layer_id++) { + const Layer* layer = object.get_layer(layer_id); + if (overlaps(layer->sharp_tails, cluster.merged_overhangs_dilated)) { + cluster.is_sharp_tail = true; break; } } - if (is_sharp_tail) - continue; - // 4. check whether the overhang cluster is cantilever (far awary from main body) - const Layer* layer = object.get_layer(cluster.min_layer); - if (layer->lower_layer == NULL) continue; - Layer* lower_layer = layer->lower_layer; - auto cluster_boundary = intersection(cluster.merged_overhangs_dilated, offset(lower_layer->lslices, scale_(0.5))); - double dist_max = 0; - Points cluster_pts; - for (auto& poly : cluster.merged_overhangs_dilated) - append(cluster_pts, poly.points); - for (auto& pt : cluster_pts) { - double dist_pt = std::numeric_limits::max(); - for (auto& poly : cluster_boundary) { - double d = poly.distance_to(pt); - dist_pt = std::min(dist_pt, d); + if (!cluster.is_sharp_tail && !cluster.is_cantilever) { + // 2. check overhang cluster size is small + cluster.is_small_overhang = false; + auto erode1 = offset_ex(cluster.merged_overhangs_dilated, -2.5 * fw_scaled); + if (area(erode1) < SQ(scale_(0.1))) { + cluster.is_small_overhang = true; } - dist_max = std::max(dist_max, dist_pt); } - if (dist_max > 5.0 * fw_scaled) - continue; + +#ifdef SUPPORT_TREE_DEBUG_TO_SVG + const Layer* layer1 = object.get_layer(cluster.min_layer); + BoundingBox bbox = get_extents(cluster.merged_overhangs_dilated); + bbox.merge(get_extents(layer1->lslices)); + SVG svg(format("SVG/overhangCluster_%s_%s_tail=%s_cantilever=%s_small=%s.svg", cluster.min_layer, layer1->print_z, cluster.is_sharp_tail, cluster.is_cantilever, cluster.is_small_overhang), bbox); + if (svg.is_opened()) { + svg.draw(layer1->lslices, "red"); + svg.draw(cluster.merged_overhangs_dilated, "blue"); + } +#endif // 5. remove small overhangs - for (auto overhangs : cluster.layer_overhangs) { - for (Polygon* poly : overhangs.second) - removed_overhang.insert(poly); + if (cluster.is_small_overhang) { + for (auto overhangs : cluster.layer_overhangs) { + for (auto* poly : overhangs.second) + removed_overhang.insert(poly); + } } } - for (size_t layer_id = this->has_raft() ? 0 : 1; layer_id < num_layers; layer_id++) { - Polygons& layer_overhangs = overhangs_per_layers[layer_id]; + for (size_t layer_id = layer_id_start; layer_id < num_layers; layer_id++) { + auto& layer_overhangs = overhangs_per_layers[layer_id]; if (layer_overhangs.empty()) continue; - bool remove_hole = false; for (int poly_idx = 0; poly_idx < layer_overhangs.size(); poly_idx++) { - Polygon* overhang = &layer_overhangs[poly_idx]; - if (overhang->is_counter_clockwise()) { - if (removed_overhang.find(overhang) != removed_overhang.end()) { - remove_hole = true; - overhang->clear(); - } - else - remove_hole = false; - } - else { - if (remove_hole) - overhang->clear(); + auto* overhang = &layer_overhangs[poly_idx]; + if (removed_overhang.find(overhang) != removed_overhang.end()) { + overhang->clear(); } } } @@ -2329,9 +2414,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ if (object.print()->canceled()) return MyLayersPtr(); - for (size_t layer_id = this->has_raft() ? 0 : 1; layer_id < num_layers; layer_id++) { + for (size_t layer_id = layer_id_start; layer_id < num_layers; layer_id++) { const Layer& layer = *object.layers()[layer_id]; - Polygons overhang_polygons = overhangs_per_layers[layer_id]; + Polygons overhang_polygons = to_polygons(overhangs_per_layers[layer_id]); Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers()[layer_id - 1]->lslices); SlicesMarginCache slices_margin; @@ -3024,6 +3109,36 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int assert(top_contacts[i]->height > 0.); #endif /* _DEBUG */ +#if 0 // #ifdef SLIC3R_DEBUG + // check bounds + std::ofstream out; + out.open("./SVG/ns_bounds.txt"); + if (out.is_open()) { + if (!top_contacts.empty()) { + out << "### Top Contacts ###" << std::endl; + for (auto& t : top_contacts) { + out << t->print_z << std::endl; + } + } + if (!bottom_contacts.empty()) { + out << "### Bottome Contacts ###" << std::endl; + for (auto& b : bottom_contacts) { + out << b->print_z << std::endl; + } + } + if (!intermediate_layers.empty()) { + out << "### Intermediate Layers ###" << std::endl; + for (auto& i : intermediate_layers) { + out << i->print_z << std::endl; + } + } + out << "### Slice Layers ###" << std::endl; + for (size_t j = 0; j < object.layers().size(); ++j) { + out << object.layers()[j]->print_z << std::endl; + } + } +#endif /* SLIC3R_DEBUG */ + return intermediate_layers; } @@ -3606,7 +3721,6 @@ static inline void fill_expolygon_generate_paths( ExPolygon &&expolygon, Fill *filler, const FillParams &fill_params, - float density, ExtrusionRole role, const Flow &flow) { @@ -3628,12 +3742,11 @@ static inline void fill_expolygons_generate_paths( ExPolygons &&expolygons, Fill *filler, const FillParams &fill_params, - float density, ExtrusionRole role, const Flow &flow) { for (ExPolygon &expoly : expolygons) - fill_expolygon_generate_paths(dst, std::move(expoly), filler, fill_params, density, role, flow); + fill_expolygon_generate_paths(dst, std::move(expoly), filler, fill_params, role, flow); } static inline void fill_expolygons_generate_paths( @@ -3647,7 +3760,7 @@ static inline void fill_expolygons_generate_paths( FillParams fill_params; fill_params.density = density; fill_params.dont_adjust = true; - fill_expolygons_generate_paths(dst, std::move(expolygons), filler, fill_params, density, role, flow); + fill_expolygons_generate_paths(dst, std::move(expolygons), filler, fill_params, role, flow); } static inline void fill_expolygons_with_sheath_generate_paths( @@ -3695,7 +3808,7 @@ static inline void fill_expolygons_with_sheath_generate_paths( } extrusion_entities_append_paths(out, polylines, erSupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height()); // Fill in the rest. - fill_expolygons_generate_paths(out, offset_ex(expoly, float(-0.4 * spacing)), filler, fill_params, density, role, flow); + fill_expolygons_generate_paths(out, offset_ex(expoly, float(-0.4 * spacing)), filler, fill_params, role, flow); if (no_sort && ! eec->empty()) dst.emplace_back(eec.release()); } @@ -4545,13 +4658,23 @@ void PrintObjectSupportMaterial::generate_toolpaths( double density = interface_as_base ? m_support_params.support_density : m_support_params.interface_density; filler_interface->spacing = interface_as_base ? m_support_params.support_material_flow.spacing() : m_support_params.support_material_interface_flow.spacing(); filler_interface->link_max_length = coord_t(scale_(filler_interface->spacing * link_max_length_factor / density)); + // BBS support more interface patterns + FillParams fill_params; + fill_params.density = density; + fill_params.dont_adjust = true; + if (m_object_config->support_interface_pattern == smipGrid) { + filler_interface->angle = Geometry::deg2rad(m_support_params.base_angle); + fill_params.dont_sort = true; + } + if (m_object_config->support_interface_pattern == smipRectilinearInterlaced) + filler_interface->layer_id = support_layer.interface_id(); fill_expolygons_generate_paths( // Destination layer_ex.extrusions, // Regions to fill union_safety_offset_ex(layer_ex.polygons_to_extrude()), // Filler and its parameters - filler_interface.get(), float(density), + filler_interface.get(), fill_params, // Extrusion parameters erSupportMaterialInterface, interface_flow); } @@ -4660,7 +4783,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( std::stable_sort(layer_cache_item.overlapping.begin(), layer_cache_item.overlapping.end(), [](auto *l1, auto *l2) { return *l1 < *l2; }); } if (! polys.empty()) - expolygons_append(support_layer.support_islands.expolygons, union_ex(polys)); + expolygons_append(support_layer.support_islands, union_ex(polys)); } // for each support_layer_id }); diff --git a/src/libslic3r/SurfaceCollection.cpp b/src/libslic3r/SurfaceCollection.cpp index ec847d2a3f..f2f0b3f97e 100644 --- a/src/libslic3r/SurfaceCollection.cpp +++ b/src/libslic3r/SurfaceCollection.cpp @@ -67,12 +67,9 @@ SurfacesPtr SurfaceCollection::filter_by_types(const SurfaceType *types, int nty void SurfaceCollection::filter_by_type(SurfaceType type, Polygons* polygons) { - for (Surfaces::iterator surface = this->surfaces.begin(); surface != this->surfaces.end(); ++surface) { - if (surface->surface_type == type) { - Polygons pp = surface->expolygon; - polygons->insert(polygons->end(), pp.begin(), pp.end()); - } - } + for (const Surface &surface : this->surfaces) + if (surface.surface_type == type) + polygons_append(*polygons, to_polygons(surface.expolygon)); } void SurfaceCollection::keep_type(const SurfaceType type) diff --git a/src/libslic3r/TreeSupport.cpp b/src/libslic3r/TreeSupport.cpp index 8ecae4069e..2eca6b343a 100644 --- a/src/libslic3r/TreeSupport.cpp +++ b/src/libslic3r/TreeSupport.cpp @@ -12,11 +12,9 @@ #include "I18N.hpp" #include -#include "Fill/FillBase.hpp" - #define _L(s) Slic3r::I18N::translate(s) -#define HEIGHT_TO_SWITCH_INFILL_DIRECTION 30 // change infill direction every 20mm +#define USE_PLAN_LAYER_HEIGHTS 1 #ifndef M_PI #define M_PI 3.1415926535897932384626433832795 @@ -29,6 +27,9 @@ // #define SUPPORT_TREE_DEBUG_TO_SVG +#ifdef SUPPORT_TREE_DEBUG_TO_SVG +#include "nlohmann/json.hpp" +#endif namespace Slic3r { #define unscale_(val) ((val) * SCALING_FACTOR) @@ -236,11 +237,15 @@ static void draw_contours_and_nodes_to_svg svg.draw_outline(outlines_below, colors[2]); // draw legend - svg.draw_text(bbox.min + Point(scale_(0), scale_(0)), ("nPoints: "+std::to_string(layer_nodes.size())+"->").c_str(), "green", 4); - svg.draw_text(bbox.min + Point(scale_(15), scale_(0)), std::to_string(lower_layer_nodes.size()).c_str(), "black", 4); - svg.draw_text(bbox.min + Point(scale_(0), scale_(1)), legends[0].c_str(), colors[0].c_str(), 4); - svg.draw_text(bbox.min + Point(scale_(0), scale_(2)), legends[1].c_str(), colors[1].c_str(), 4); - svg.draw_text(bbox.min + Point(scale_(0), scale_(3)), legends[2].c_str(), colors[2].c_str(), 4); + if (!lower_layer_nodes.empty()) { + svg.draw_text(bbox.min + Point(scale_(0), scale_(0)), format("nPoints: %1%->%2%",layer_nodes.size(), lower_layer_nodes.size()).c_str(), "green", 2); + } + else { + svg.draw_text(bbox.min + Point(scale_(0), scale_(0)), ("nPoints: " + std::to_string(layer_nodes.size())).c_str(), "green", 2); + } + svg.draw_text(bbox.min + Point(scale_(0), scale_(2)), legends[0].c_str(), colors[0].c_str(), 2); + svg.draw_text(bbox.min + Point(scale_(0), scale_(4)), legends[1].c_str(), colors[1].c_str(), 2); + svg.draw_text(bbox.min + Point(scale_(0), scale_(6)), legends[2].c_str(), colors[2].c_str(), 2); // draw layer nodes svg.draw(layer_pts, "green", coord_t(scale_(0.1))); @@ -623,32 +628,11 @@ static bool is_inside_ex(const ExPolygons &polygons, const Point &pt) return false; } -Point projection_onto_ex(const ExPolygons& polygons, Point from) -{ - profiler.tic(); - Point projected_pt; - double min_dist = std::numeric_limits::max(); - - for (auto poly : polygons) { - for (int i = 0; i < poly.num_contours(); i++) { - Point p = from.projection_onto(poly.contour_or_hole(i)); - double dist = (from - p).cast().squaredNorm(); - if (dist < min_dist) { - projected_pt = p; - min_dist = dist; - } - } - } - - profiler.stage_add(STAGE_projection_onto_ex, true); - return projected_pt; -} - static bool move_out_expolys(const ExPolygons& polygons, Point& from, double distance, double max_move_distance) { Point from0 = from; ExPolygons polys_dilated = union_ex(offset_ex(polygons, scale_(distance))); - Point pt = projection_onto_ex(polys_dilated, from);// find_closest_ex(from, polys_dilated); + Point pt = projection_onto(polys_dilated, from);// find_closest_ex(from, polys_dilated); Point outward_dir = pt - from; Point pt_max = from + normal(outward_dir, scale_(max_move_distance)); double dist2 = vsize2_with_unscale(outward_dir); @@ -683,9 +667,12 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p : m_object(&object), m_slicing_params(slicing_params), m_object_config(&object.config()) { m_raft_layers = slicing_params.base_raft_layers + slicing_params.interface_raft_layers; - + support_type = m_object_config->support_type; + support_style = m_object_config->support_style; + if (support_style == smsDefault) + support_style = smsTreeHybrid; SupportMaterialPattern support_pattern = m_object_config->support_base_pattern; - if (m_object_config->support_style == smsTreeHybrid && support_pattern == smpDefault) support_pattern = smpRectilinear; + if (support_style == smsTreeHybrid && support_pattern == smpDefault) support_pattern = smpRectilinear; m_support_params.base_fill_pattern = support_pattern == smpLightning ? ipLightning : support_pattern == smpHoneycomb ? ipHoneycomb : @@ -693,13 +680,18 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p ipSupportBase; m_support_params.interface_fill_pattern = (m_support_params.interface_density > 0.95 ? ipRectilinear : ipSupportBase); - m_support_params.contact_fill_pattern = (m_object_config->support_interface_pattern == smipAuto && m_slicing_params.soluble_interface) || - m_object_config->support_interface_pattern == smipConcentric ? - ipConcentric : - (m_support_params.interface_density > 0.95 ? ipRectilinear : ipSupportBase); + if (m_object_config->support_interface_pattern == smipGrid) + m_support_params.contact_fill_pattern = ipGrid; + else if (m_object_config->support_interface_pattern == smipRectilinearInterlaced) + m_support_params.contact_fill_pattern = ipRectilinear; + else + m_support_params.contact_fill_pattern = (m_object_config->support_interface_pattern == smipAuto && m_slicing_params.soluble_interface) || + m_object_config->support_interface_pattern == smipConcentric ? + ipConcentric : + (m_support_params.interface_density > 0.95 ? ipRectilinear : ipSupportBase); m_support_params.support_extrusion_width = m_object_config->support_line_width.value > 0 ? m_object_config->support_line_width : m_object_config->line_width; - support_type = m_object_config->support_type; - is_slim = is_tree_slim(support_type, m_object_config->support_style); + is_slim = is_tree_slim(support_type, support_style); + is_strong = is_tree(support_type) && support_style == smsTreeStrong; MAX_BRANCH_RADIUS = 10.0; tree_support_branch_diameter_angle = 5.0;//is_slim ? 10.0 : 5.0; // by default tree support needs no infill, unless it's tree hybrid which contains normal nodes. @@ -717,7 +709,7 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p #define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0. -void TreeSupport::detect_overhangs() +void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only) { // overhangs are already detected if (m_object->support_layer_count() >= m_object->layer_count()) @@ -726,10 +718,8 @@ void TreeSupport::detect_overhangs() // Clear and create Tree Support Layers m_object->clear_support_layers(); m_object->clear_tree_support_preview_cache(); - create_tree_support_layers(); - const PrintObjectConfig& config = m_object->config(); SupportType stype = support_type; const coordf_t radius_sample_resolution = g_config_tree_support_collision_resolution; @@ -745,20 +735,22 @@ void TreeSupport::detect_overhangs() // a region is considered well supported if the number of layers below it exceeds this threshold const int thresh_layers_below = 10 / config.layer_height; double obj_height = m_object->size().z(); + // +1 makes the threshold inclusive + double thresh_angle = config.support_threshold_angle.value > EPSILON ? config.support_threshold_angle.value + 1 : 30; + thresh_angle = std::min(thresh_angle, 89.); // should be smaller than 90 + const double threshold_rad = Geometry::deg2rad(thresh_angle); - struct ExPolygonComp { - bool operator()(const ExPolygon& a, const ExPolygon& b) const { - return &a < &b; - } - }; // for small overhang removal struct OverhangCluster { std::map layer_overhangs; ExPolygons merged_poly; + BoundingBox merged_bbox; int min_layer = 1e7; int max_layer = 0; coordf_t offset = 0; bool is_cantilever = false; + bool is_sharp_tail = false; + bool is_small_overhang = false; OverhangCluster(const ExPolygon* expoly, int layer_nr) { push_back(expoly, layer_nr); } @@ -769,6 +761,7 @@ void TreeSupport::detect_overhangs() merged_poly = union_ex(merged_poly, dilate1); min_layer = std::min(min_layer, layer_nr); max_layer = std::max(max_layer, layer_nr); + merged_bbox.merge(get_extents(dilate1)); } int height() { return max_layer - min_layer + 1; @@ -781,376 +774,314 @@ void TreeSupport::detect_overhangs() this->offset = offset; auto dilate1 = offset_ex(region, offset); - return !intersection_ex(*overhang, dilate1).empty(); + BoundingBox bbox = get_extents(dilate1); + if (!merged_bbox.overlap(bbox)) + return false; + return overlaps({ *overhang }, dilate1); + } + // it's basically the combination of push_back and intersects, but saves an offset_ex + bool push_back_if_intersects(const ExPolygon& region, int layer_nr, coordf_t offset) { + bool is_intersect = false; + ExPolygons dilate1; + BoundingBox bbox; + do { + if (layer_nr < 1) break; + auto it = layer_overhangs.find(layer_nr - 1); + if (it == layer_overhangs.end()) break; + const ExPolygon* overhang = it->second; + + this->offset = offset; + dilate1 = offset_ex(region, offset); + if (dilate1.empty()) break; + bbox = get_extents(dilate1); + if (!merged_bbox.overlap(bbox)) + break; + is_intersect = overlaps({ *overhang }, dilate1); + } while (0); + if (is_intersect) { + layer_overhangs.emplace(layer_nr, ®ion); + merged_poly = union_ex(merged_poly, dilate1); + min_layer = std::min(min_layer, layer_nr); + max_layer = std::max(max_layer, layer_nr); + merged_bbox.merge(bbox); + } + return is_intersect; } }; std::vector overhangClusters; - std::map overhang2clusterInd; - // for sharp tail detection - struct RegionCluster { - std::map layer_regions; - ExPolygon base; - BoundingBox bbox; - int min_layer = 1e7; - int max_layer = 0; - RegionCluster(const ExPolygon* expoly, int layer_nr) { - push_back(expoly, layer_nr); - } - void push_back(const ExPolygon* expoly, int layer_nr) { - if (layer_regions.empty()) { - base = *expoly; - bbox = get_extents(base); - } - layer_regions.emplace(layer_nr, expoly); - bbox.merge(get_extents(*expoly)); - min_layer = std::min(min_layer, layer_nr); - max_layer = std::max(max_layer, layer_nr); - } - int height() { - return max_layer - min_layer + 1; - } - bool intersects(const ExPolygon& region, int layer_nr, coordf_t offset) { - if (layer_nr < 1) return false; - auto it = layer_regions.find(layer_nr - 1); - if (it == layer_regions.end()) return false; - const ExPolygon* region_lower = it->second; - return !intersection_ex(*region_lower, region).empty(); - } - }; - std::vector regionClusters; - std::map region2clusterInd; - auto find_and_insert_cluster = [](auto& regionClusters, auto& region2clusterInd, const ExPolygon& region, int layer_nr, coordf_t offset) { - bool found = false; - for (int i = 0; i < regionClusters.size();i++) { - auto& cluster = regionClusters[i]; - if (cluster.intersects(region, layer_nr, offset)) { - cluster.push_back(®ion, layer_nr); - region2clusterInd.emplace(®ion, i); - found = true; + auto find_and_insert_cluster = [](auto ®ionClusters, const ExPolygon ®ion, int layer_nr, coordf_t offset) { + OverhangCluster *cluster = nullptr; + for (int i = 0; i < regionClusters.size(); i++) { + auto cluster_i = ®ionClusters[i]; + if (cluster_i->push_back_if_intersects(region, layer_nr, offset)) { + cluster = cluster_i; break; } } - if (!found) { - regionClusters.emplace_back(®ion, layer_nr); - region2clusterInd.emplace(®ion, regionClusters.size() - 1); + if (!cluster) { + cluster = ®ionClusters.emplace_back(®ion, layer_nr); } + return cluster; }; - // main part of sharptail detections - if (is_tree(stype)) - { - double threshold_rad = (config.support_threshold_angle.value < EPSILON ? 30 : config.support_threshold_angle.value+1) * M_PI / 180.; - ExPolygons regions_well_supported; - std::map region_layers_below; - ExPolygons lower_overhang_dilated; - - for (size_t layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++){ - if (m_object->print()->canceled()) - break; - - if (!is_auto(stype) && layer_nr > enforce_support_layers) - continue; - Layer* layer = m_object->get_layer(layer_nr); - for (auto& slice : layer->lslices) - find_and_insert_cluster(regionClusters, region2clusterInd, slice, layer_nr, 0); + if (!is_tree(stype)) return; - if (layer->lower_layer == nullptr) { - for (auto& slice : layer->lslices) { - auto bbox_size = get_extents(slice).size(); - if (/*slice.area() > area_thresh_well_supported || */ - (bbox_size.x()>length_thresh_well_supported && bbox_size.y()>length_thresh_well_supported)) - regions_well_supported.emplace_back(slice); - else if(g_config_support_sharp_tails){ - layer->sharp_tails.push_back(slice); - layer->sharp_tails_height.insert({ &slice, layer->height }); + max_cantilever_dist = 0; + + // main part of overhang detection can be parallel + tbb::parallel_for(tbb::blocked_range(0, m_object->layer_count()), + [&](const tbb::blocked_range& range) { + for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++) { + if (m_object->print()->canceled()) + break; + + if (!is_auto(stype) && layer_nr > enforce_support_layers) + continue; + + Layer* layer = m_object->get_layer(layer_nr); + + if (layer->lower_layer == nullptr) { + for (auto& slice : layer->lslices) { + auto bbox_size = get_extents(slice).size(); + if (!((bbox_size.x() > length_thresh_well_supported && bbox_size.y() > length_thresh_well_supported)) + && g_config_support_sharp_tails) { + layer->sharp_tails.push_back(slice); + layer->sharp_tails_height.insert({ &slice, layer->height }); + } + } + continue; + } + + Layer* lower_layer = layer->lower_layer; + coordf_t lower_layer_offset = layer_nr < enforce_support_layers ? -0.15 * extrusion_width : (float)lower_layer->height / tan(threshold_rad); + coordf_t support_offset_scaled = scale_(lower_layer_offset); + // Filter out areas whose diameter that is smaller than extrusion_width. Do not use offset2() for this purpose! + ExPolygons lower_polys; + for (const ExPolygon& expoly : lower_layer->lslices) { + if (!offset_ex(expoly, -extrusion_width_scaled / 2).empty()) { + lower_polys.emplace_back(expoly); } } - continue; - } - - Layer* lower_layer = layer->lower_layer; - coordf_t lower_layer_offset = layer_nr < enforce_support_layers ? -0.15 * extrusion_width : (float)lower_layer->height / tan(threshold_rad); - coordf_t support_offset_scaled = scale_(lower_layer_offset); - // Filter out areas whose diameter that is smaller than extrusion_width. Do not use offset2() for this purpose! - ExPolygons lower_polys;// = offset2_ex(lower_layer->lslices, -extrusion_width_scaled / 2, extrusion_width_scaled / 2); - for (const ExPolygon& expoly : lower_layer->lslices) { - if (!offset_ex(expoly, -extrusion_width_scaled / 2).empty()) { - lower_polys.emplace_back(expoly); - } - } - ExPolygons curr_polys;// = offset2_ex(layer->lslices, -extrusion_width_scaled / 2, extrusion_width_scaled / 2); - for (const ExPolygon& expoly : layer->lslices) { - if (!offset_ex(expoly, -extrusion_width_scaled / 2).empty()) { - curr_polys.emplace_back(expoly); - } - } - - // normal overhang - ExPolygons lower_layer_offseted = offset_ex(lower_polys, support_offset_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS); - ExPolygons overhang_areas = std::move(diff_ex(curr_polys, lower_layer_offseted)); - - overhang_areas.erase(std::remove_if(overhang_areas.begin(), overhang_areas.end(), - [extrusion_width_scaled](ExPolygon &area) { return offset_ex(area, -0.1 * extrusion_width_scaled).empty(); }), - overhang_areas.end()); - - - ExPolygons overhangs_sharp_tail; - if (is_auto(stype) && g_config_support_sharp_tails) - { -#if 0 - // detect sharp tail and add more supports around - for (auto& lower_region : lower_layer_offseted) { - auto radius = get_extents(lower_region).radius(); - auto out_of_well_supported_region = offset_ex(diff_ex({ lower_region }, regions_well_supported), -extrusion_width_scaled); - auto bbox_size = get_extents(out_of_well_supported_region).size(); - double area_inter = area(intersection_ex({ lower_region }, regions_well_supported)); - if ((area_inter==0 || - ((bbox_size.x()> extrusion_width_scaled && bbox_size.y() > extrusion_width_scaled) && area_inter < area_thresh_well_supported ) ) - /*&& (obj_height - scale_(layer->slice_z)) > get_extents(lower_region).radius() * 5*/) { - auto lower_region_unoffseted = offset_ex(lower_region, -support_offset_scaled); - if (!lower_region_unoffseted.empty()) - lower_region = lower_region_unoffseted.front(); + ExPolygons curr_polys; + for (const ExPolygon& expoly : layer->lslices) { + if (!offset_ex(expoly, -extrusion_width_scaled / 2).empty()) { + curr_polys.emplace_back(expoly); } } - if (!lower_layer_offseted.empty()) { - overhangs_sharp_tail = std::move(diff_ex(curr_polys, lower_layer_offseted)); - //overhangs_sharp_tail = std::move(offset2_ex(overhangs_sharp_tail, -0.1 * extrusion_width_scaled, 0.1 * extrusion_width_scaled)); - overhangs_sharp_tail = diff_ex(overhangs_sharp_tail, overhang_areas); - } - if (!overhangs_sharp_tail.empty()) { - append(layer->sharp_tails, overhangs_sharp_tail); - overhang_areas = union_ex(overhang_areas, overhangs_sharp_tail); - } -#else - // BBS detect sharp tail - const ExPolygons& lower_layer_sharptails = lower_layer->sharp_tails; - auto& lower_layer_sharptails_height = lower_layer->sharp_tails_height; - for (ExPolygon& expoly : layer->lslices) { - bool is_sharp_tail = false; - float accum_height = layer->height; - do { + + // normal overhang + ExPolygons lower_layer_offseted = offset_ex(lower_polys, support_offset_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS); + ExPolygons overhang_areas = std::move(diff_ex(curr_polys, lower_layer_offseted)); + + overhang_areas.erase(std::remove_if(overhang_areas.begin(), overhang_areas.end(), + [extrusion_width_scaled](ExPolygon& area) { return offset_ex(area, -0.1 * extrusion_width_scaled).empty(); }), + overhang_areas.end()); + + + ExPolygons overhangs_sharp_tail; + if (is_auto(stype) && g_config_support_sharp_tails) + { + // BBS detect sharp tail + const ExPolygons& lower_layer_sharptails = lower_layer->sharp_tails; + auto& lower_layer_sharptails_height = lower_layer->sharp_tails_height; + for (ExPolygon& expoly : layer->lslices) { + bool is_sharp_tail = false; // 1. nothing below // this is a sharp tail region if it's small but non-ignorable - if (intersection_ex({expoly}, lower_polys).empty()) { - is_sharp_tail = expoly.area() < area_thresh_well_supported && !offset_ex(expoly,-0.5*extrusion_width_scaled).empty(); - break; + if (!overlaps(offset_ex(expoly, 0.5 * extrusion_width_scaled), lower_polys)) { + is_sharp_tail = expoly.area() < area_thresh_well_supported && !offset_ex(expoly, -0.1 * extrusion_width_scaled).empty(); } - // 2. something below - // check whether this is above a sharp tail region. + if (is_sharp_tail) { + ExPolygons overhang = diff_ex({ expoly }, lower_layer->lslices); + layer->sharp_tails.push_back(expoly); + layer->sharp_tails_height.insert({ &expoly, layer->height }); + append(overhang_areas, overhang); - // 2.1 If no sharp tail below, this is considered as common region. - ExPolygons supported_by_lower = intersection_ex({expoly}, lower_layer_sharptails); - if (supported_by_lower.empty()) { - is_sharp_tail = false; - break; - } - - // 2.2 If sharp tail below, check whether it support this region enough. - float supported_area = area(supported_by_lower); - BoundingBox bbox = get_extents(supported_by_lower); -#if 0 - // judge by area isn't reliable, failure cases include 45 degree rotated cube - if (supported_area > area_thresh_well_supported) { - is_sharp_tail = false; - break; - } -#endif - if (bbox.size().x() > length_thresh_well_supported && bbox.size().y() > length_thresh_well_supported) { - is_sharp_tail = false; - break; - } - - // 2.3 check whether sharp tail exceed the max height - for (auto &lower_sharp_tail_height : lower_layer_sharptails_height) { - if (!intersection_ex(*lower_sharp_tail_height.first, expoly).empty()) { - accum_height += lower_sharp_tail_height.second; - break; - } - } - if (accum_height >= sharp_tail_max_support_height) { - is_sharp_tail = false; - break; - } - - // 2.4 if the area grows fast than threshold, it get connected to other part or - // it has a sharp slop and will be auto supported. - ExPolygons new_overhang_expolys = diff_ex({expoly}, lower_layer_sharptails); - if ((get_extents(new_overhang_expolys).size()-get_extents(lower_layer_sharptails).size()).both_comp(Point(scale_(5),scale_(5)),">") || !offset_ex(new_overhang_expolys, -5.0 * extrusion_width_scaled).empty()) { - is_sharp_tail = false; - break; - } - - // 2.5 mark the expoly as sharptail - is_sharp_tail = true; - } while (0); - - if (is_sharp_tail) { - ExPolygons overhang = diff_ex({expoly}, lower_layer->lslices); - layer->sharp_tails.push_back(expoly); - layer->sharp_tails_height.insert({ &expoly, accum_height }); - append(overhang_areas, overhang); - - if (!overhang.empty()) - has_sharp_tails = true; -#ifdef SUPPORT_TREE_DEBUG_TO_SVG - SVG svg(get_svg_filename(std::to_string(layer->print_z), "sharp_tail"), m_object->bounding_box()); - if (svg.is_opened()) svg.draw(overhang, "yellow"); -#endif + if (!overhang.empty()) + has_sharp_tails = true; + } } } -#endif - } - - if (max_bridge_length > 0 && overhang_areas.size()>0) { - // do not break bridge for normal part in TreeHybrid - bool break_bridge = !(config.support_style == smsTreeHybrid && area(overhang_areas) > m_support_params.thresh_big_overhang); - m_object->remove_bridges_from_contacts(lower_layer, layer, extrusion_width_scaled, &overhang_areas, max_bridge_length, break_bridge); - } - - SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); - for (ExPolygon& poly : overhang_areas) { - if (!offset_ex(poly, -0.1 * extrusion_width_scaled).empty()) + SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); + for (ExPolygon& poly : overhang_areas) { + if (offset_ex(poly, -0.1 * extrusion_width_scaled).empty()) continue; ts_layer->overhang_areas.emplace_back(poly); - } - if (is_auto(stype) && g_config_remove_small_overhangs) { - for (auto& overhang : ts_layer->overhang_areas) { - find_and_insert_cluster(overhangClusters, overhang2clusterInd, overhang, layer_nr, extrusion_width_scaled); - } - } - - if (is_auto(stype) && /*g_config_support_sharp_tails*/0) - { // update well supported regions - ExPolygons regions_well_supported2; - // regions intersects with lower regions_well_supported or large support are also well supported - auto inters = intersection_ex(layer->lslices, regions_well_supported); - auto inters2 = intersection_ex(layer->lslices, ts_layer->overhang_areas); - inters.insert(inters.end(), inters2.begin(), inters2.end()); - for (auto inter : inters) { - auto bbox_size = get_extents(inter).size(); - if (inter.area() >= area_thresh_well_supported - || (bbox_size.x()>length_thresh_well_supported && bbox_size.y()>length_thresh_well_supported) ) + // check cantilever { - auto tmp = offset_ex(inter, support_offset_scaled); - if (!tmp.empty()) { - // if inter is a single line with only 2 valid points, clipper will return empty - regions_well_supported2.emplace_back(std::move(tmp[0])); + auto cluster_boundary_ex = intersection_ex(poly, offset_ex(lower_layer->lslices, scale_(0.5))); + Polygons cluster_boundary = to_polygons(cluster_boundary_ex); + if (cluster_boundary.empty()) continue; + double dist_max = 0; + for (auto& pt : poly.contour.points) { + double dist_pt = std::numeric_limits::max(); + for (auto& ply : cluster_boundary) { + double d = ply.distance_to(pt); + dist_pt = std::min(dist_pt, d); + } + dist_max = std::max(dist_max, dist_pt); + } + if (dist_max > scale_(3)) { // is cantilever if the farmost point is larger than 3mm away from base + max_cantilever_dist = std::max(max_cantilever_dist, dist_max); + layer->cantilevers.emplace_back(poly); + BOOST_LOG_TRIVIAL(debug) << "found a cantilever cluster. layer_nr=" << layer_nr << dist_max; + has_cantilever = true; } } } - - // experimental: regions high enough is also well supported - for (auto& region : layer->lslices) { - auto cluster = regionClusters[region2clusterInd[®ion]]; - if (layer_nr - cluster.min_layer > thresh_layers_below) - regions_well_supported2.push_back(region); - } - - regions_well_supported = union_ex(regions_well_supported2); -#ifdef SUPPORT_TREE_DEBUG_TO_SVG - if (!regions_well_supported.empty()) { - SVG svg(get_svg_filename(std::to_string(layer->print_z), "regions_well_supported"), m_object->bounding_box()); - if (svg.is_opened()) - svg.draw(regions_well_supported, "yellow"); - } -#endif } } + ); // end tbb::parallel_for + + BOOST_LOG_TRIVIAL(info) << "max_cantilever_dist=" << max_cantilever_dist; + + // check if the sharp tails should be extended higher + if (is_auto(stype) && g_config_support_sharp_tails && !detect_first_sharp_tail_only) { + for (size_t layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++) { + if (m_object->print()->canceled()) + break; + + Layer* layer = m_object->get_layer(layer_nr); + SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); + Layer* lower_layer = layer->lower_layer; + // skip if: + // 1) if the current layer is already detected as sharp tails + // 2) lower layer has no sharp tails + if (!lower_layer || layer->sharp_tails.empty() == false || lower_layer->sharp_tails.empty() == true) + continue; + + // BBS detect sharp tail + const ExPolygons& lower_layer_sharptails = lower_layer->sharp_tails; + auto& lower_layer_sharptails_height = lower_layer->sharp_tails_height; + for (ExPolygon& expoly : layer->lslices) { + bool is_sharp_tail = false; + float accum_height = layer->height; + do { + // 2. something below + // check whether this is above a sharp tail region. + + // 2.1 If no sharp tail below, this is considered as common region. + ExPolygons supported_by_lower = intersection_ex({ expoly }, lower_layer_sharptails); + if (supported_by_lower.empty()) { + is_sharp_tail = false; + break; + } + + // 2.2 If sharp tail below, check whether it support this region enough. +#if 0 + // judge by area isn't reliable, failure cases include 45 degree rotated cube + float supported_area = area(supported_by_lower); + if (supported_area > area_thresh_well_supported) { + is_sharp_tail = false; + break; + } +#endif + BoundingBox bbox = get_extents(supported_by_lower); + if (bbox.size().x() > length_thresh_well_supported && bbox.size().y() > length_thresh_well_supported) { + is_sharp_tail = false; + break; + } + + // 2.3 check whether sharp tail exceed the max height + for (auto& lower_sharp_tail_height : lower_layer_sharptails_height) { + if (lower_sharp_tail_height.first->overlaps(expoly)) { + accum_height += lower_sharp_tail_height.second; + break; + } + } + if (accum_height >= sharp_tail_max_support_height) { + is_sharp_tail = false; + break; + } + + // 2.4 if the area grows fast than threshold, it get connected to other part or + // it has a sharp slop and will be auto supported. + ExPolygons new_overhang_expolys = diff_ex({ expoly }, lower_layer_sharptails); + if ((get_extents(new_overhang_expolys).size() - get_extents(lower_layer_sharptails).size()).both_comp(Point(scale_(5), scale_(5)), ">") || !offset_ex(new_overhang_expolys, -5.0 * extrusion_width_scaled).empty()) { + is_sharp_tail = false; + break; + } + + // 2.5 mark the expoly as sharptail + is_sharp_tail = true; + } while (0); + + if (is_sharp_tail) { + ExPolygons overhang = diff_ex({ expoly }, lower_layer->lslices); + layer->sharp_tails.push_back(expoly); + layer->sharp_tails_height.insert({ &expoly, accum_height }); + append(ts_layer->overhang_areas, overhang); + + if (!overhang.empty()) + has_sharp_tails = true; +#ifdef SUPPORT_TREE_DEBUG_TO_SVG + SVG svg(get_svg_filename(std::to_string(layer->print_z), "sharp_tail"), m_object->bounding_box()); + if (svg.is_opened()) svg.draw(overhang, "yellow"); +#endif + } + + } + } + } + + // group overhang clusters + for (size_t layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++) { + if (m_object->print()->canceled()) + break; + SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); + Layer* layer = m_object->get_layer(layer_nr); + for (auto& overhang : ts_layer->overhang_areas) { + OverhangCluster* cluster = find_and_insert_cluster(overhangClusters, overhang, layer_nr, extrusion_width_scaled); + if (overlaps({ overhang },layer->cantilevers)) + cluster->is_cantilever = true; + } } auto enforcers = m_object->slice_support_enforcers(); auto blockers = m_object->slice_support_blockers(); m_object->project_and_append_custom_facets(false, EnforcerBlockerType::ENFORCER, enforcers); m_object->project_and_append_custom_facets(false, EnforcerBlockerType::BLOCKER, blockers); - - // check whether the overhang cluster is cantilever (far awary from main body) - for (auto& cluster : overhangClusters) { - Layer* layer = m_object->get_layer(cluster.min_layer); - if (layer->lower_layer == NULL) continue; - Layer* lower_layer = layer->lower_layer; - auto cluster_boundary = intersection(cluster.merged_poly, offset(lower_layer->lslices, scale_(0.5))); - double dist_max = 0; - Points cluster_pts; - for (auto& poly : cluster.merged_poly) - append(cluster_pts, poly.contour.points); - for (auto& pt : cluster_pts) { - double dist_pt = std::numeric_limits::max(); - for (auto& poly : cluster_boundary) { - double d = poly.distance_to(pt); - dist_pt = std::min(dist_pt, d); - } - dist_max = std::max(dist_max, dist_pt); - } - if (dist_max > scale_(3)) { // this cluster is cantilever if the farmost point is larger than 3mm away from base - for (auto it = cluster.layer_overhangs.begin(); it != cluster.layer_overhangs.end(); it++) { - int layer_nr = it->first; - auto p_overhang = it->second; - m_object->get_layer(layer_nr)->cantilevers.emplace_back(*p_overhang); - } - cluster.is_cantilever = true; - } - } - if (is_auto(stype) && g_config_remove_small_overhangs) { if (blockers.size() < m_object->layer_count()) blockers.resize(m_object->layer_count()); for (auto& cluster : overhangClusters) { - auto erode1 = offset_ex(cluster.merged_poly, -1.5*extrusion_width_scaled); - //DEBUG - bool save_poly = false; - if (save_poly) { - erode1 = offset_ex(cluster.merged_poly, -1*extrusion_width_scaled); - double a = area(erode1); - erode1[0].contour.remove_duplicate_points(); - a = area(erode1); - auto tmp = offset_ex(erode1, extrusion_width_scaled); - SVG svg("SVG/merged_poly_"+std::to_string(cluster.min_layer)+".svg", m_object->bounding_box()); - if (svg.is_opened()) { - svg.draw_outline(cluster.merged_poly, "yellow"); - svg.draw_outline(erode1, "yellow"); - } - } - - // 1. check overhang span size is smaller than 3mm - //auto bbox_size = get_extents(cluster.merged_poly).size(); - //const double dimension_limit = scale_(3.0) + 2 * extrusion_width_scaled; - //if (bbox_size.x() > dimension_limit || bbox_size.y() > dimension_limit) - // continue; - - // 2. check overhang cluster size is smaller than 3.0 * fw_scaled - if (area(erode1) > SQ(scale_(0.1))) - continue; - // 3. check whether the small overhang is sharp tail - bool is_sharp_tail = false; - for (size_t layer_id = cluster.min_layer; layer_id < cluster.max_layer; layer_id++) { + cluster.is_sharp_tail = false; + for (size_t layer_id = cluster.min_layer; layer_id <= cluster.max_layer; layer_id++) { Layer* layer = m_object->get_layer(layer_id); - if (!intersection_ex(layer->sharp_tails, cluster.merged_poly).empty()) { - is_sharp_tail = true; + if (overlaps(layer->sharp_tails, cluster.merged_poly)) { + cluster.is_sharp_tail = true; break; } } - if (is_sharp_tail) continue; - // 4. check whether the overhang cluster is cantilever - if (cluster.is_cantilever) continue; + if (!cluster.is_sharp_tail && !cluster.is_cantilever) { + // 2. check overhang cluster size is smaller than 3.0 * fw_scaled + auto erode1 = offset_ex(cluster.merged_poly, -1.5 * extrusion_width_scaled); + cluster.is_small_overhang = area(erode1) < SQ(scale_(0.1)); + } + +#ifdef SUPPORT_TREE_DEBUG_TO_SVG + const Layer* layer1 = m_object->get_layer(cluster.min_layer); + BoundingBox bbox = cluster.merged_bbox; + bbox.merge(get_extents(layer1->lslices)); + SVG svg(format("SVG/overhangCluster_%s_%s_tail=%s_cantilever=%s_small=%s.svg", cluster.min_layer, layer1->print_z, cluster.is_sharp_tail, cluster.is_cantilever, cluster.is_small_overhang), bbox); + if (svg.is_opened()) { + svg.draw(layer1->lslices, "red"); + svg.draw(cluster.merged_poly, "blue"); + } +#endif + + if (!cluster.is_small_overhang) + continue; for (auto it = cluster.layer_overhangs.begin(); it != cluster.layer_overhangs.end(); it++) { int layer_nr = it->first; auto p_overhang = it->second; blockers[layer_nr].push_back(p_overhang->contour); - // auto dilate1 = offset_ex(*p_overhang, extrusion_width_scaled); - // auto erode1 = offset_ex(*p_overhang, -extrusion_width_scaled); - // Layer* layer = m_object->get_layer(layer_nr); - // auto inter_with_others = intersection_ex(dilate1, diff_ex(layer->lslices, *p_overhang)); - //// the following cases are small overhangs: - //// 1) overhang is single line (erode1.empty()==true) - //// 2) overhang is not island (intersects with others) - // if (erode1.empty() && !inter_with_others.empty()) - // blockers[layer_nr].push_back(p_overhang->contour); } } } @@ -1162,8 +1093,9 @@ void TreeSupport::detect_overhangs() SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); auto layer = m_object->get_layer(layer_nr); + auto lower_layer = layer->lower_layer; if (support_critical_regions_only) { - auto lower_layer = layer->lower_layer; + ts_layer->overhang_areas.clear(); if (lower_layer == nullptr) ts_layer->overhang_areas = layer->sharp_tails; else @@ -1177,6 +1109,12 @@ void TreeSupport::detect_overhangs() ts_layer->overhang_areas = diff_ex(ts_layer->overhang_areas, offset_ex(blocker, scale_(radius_sample_resolution))); } + if (max_bridge_length > 0 && ts_layer->overhang_areas.size() > 0 && lower_layer) { + // do not break bridge for normal part in TreeHybrid + bool break_bridge = !(support_style == smsTreeHybrid && area(ts_layer->overhang_areas) > m_support_params.thresh_big_overhang); + m_object->remove_bridges_from_contacts(lower_layer, layer, extrusion_width_scaled, &ts_layer->overhang_areas, max_bridge_length, break_bridge); + } + for (auto &area : ts_layer->overhang_areas) { ts_layer->overhang_types.emplace(&area, SupportLayer::Detected); } @@ -1202,7 +1140,7 @@ void TreeSupport::detect_overhangs() if (layer->overhang_areas.empty()) continue; - SVG svg(get_svg_filename(std::to_string(layer->print_z), "overhang_areas"), m_object->bounding_box()); + SVG svg(format("SVG/overhang_areas_%s.svg", layer->print_z), m_object->bounding_box()); if (svg.is_opened()) { svg.draw_outline(m_object->get_layer(layer->id())->lslices, "yellow"); svg.draw(layer->overhang_areas, "red"); @@ -1379,14 +1317,15 @@ static void make_perimeter_and_infill(ExtrusionEntitiesPtr& dst, const Print& pr FillParams fill_params; fill_params.density = support_density; fill_params.dont_adjust = true; - ExPolygons to_infill = offset_ex(support_area, -0.5f * float(flow.scaled_spacing()), jtSquare); + ExPolygons to_infill = support_area_new; std::vector fill_boxes = fill_expolygons_generate_paths(dst, std::move(to_infill), filler_support, fill_params, role, flow); // allow wall_count to be zero, which means only draw infill if (wall_count == 0) { for (auto fill_bbox : fill_boxes) { - if (filler_support->angle == 0) { + // extend bounding box on x-axis + if (cos(filler_support->angle)>=sin(filler_support->angle)) { fill_bbox.min[0] -= scale_(10); fill_bbox.max[0] += scale_(10); } @@ -1528,17 +1467,12 @@ void TreeSupport::generate_toolpaths() } BoundingBox bbox_object(Point(-scale_(1.), -scale_(1.0)), Point(scale_(1.), scale_(1.))); - auto obj_size = m_object->size(); - bool obj_is_vertical = obj_size.x() < obj_size.y(); - int num_layers_to_change_infill_direction = int(HEIGHT_TO_SWITCH_INFILL_DIRECTION / object_config.layer_height.value); // change direction every 30mm std::shared_ptr filler_interface = std::shared_ptr(Fill::new_from_type(m_support_params.contact_fill_pattern)); std::shared_ptr filler_Roof1stLayer = std::shared_ptr(Fill::new_from_type(ipRectilinear)); - std::shared_ptr filler_support = std::shared_ptr(Fill::new_from_type(m_support_params.base_fill_pattern)); filler_interface->set_bounding_box(bbox_object); filler_Roof1stLayer->set_bounding_box(bbox_object); - filler_support->set_bounding_box(bbox_object); - filler_interface->angle = Geometry::deg2rad(object_config.support_angle.value + 90.);//(1 - obj_is_vertical) * M_PI_2;//((1-obj_is_vertical) + int(layer_id / num_layers_to_change_infill_direction)) * M_PI_2;;//layer_id % 2 ? 0 : M_PI_2; + filler_interface->angle = Geometry::deg2rad(object_config.support_angle.value + 90.); filler_Roof1stLayer->angle = Geometry::deg2rad(object_config.support_angle.value + 90.); // generate tree support tool paths @@ -1556,7 +1490,6 @@ void TreeSupport::generate_toolpaths() Flow support_flow(support_extrusion_width, ts_layer->height, nozzle_diameter); coordf_t support_spacing = object_config.support_base_pattern_spacing.value + support_flow.spacing(); coordf_t support_density = std::min(1., support_flow.spacing() / support_spacing); - ts_layer->support_fills.no_sort = false; for (auto& area_group : ts_layer->area_groups) { @@ -1603,30 +1536,38 @@ void TreeSupport::generate_toolpaths() // roof_areas fill_params.density = interface_density; filler_interface->spacing = m_support_material_interface_flow.spacing(); + if (m_object_config->support_interface_pattern == smipGrid) { + filler_interface->angle = Geometry::deg2rad(object_config.support_angle.value); + fill_params.dont_sort = true; + } + if (m_object_config->support_interface_pattern == smipRectilinearInterlaced) + filler_interface->layer_id = round(area_group.dist_to_top / ts_layer->height); fill_expolygons_generate_paths(ts_layer->support_fills.entities, std::move(polys), filler_interface.get(), fill_params, erSupportMaterialInterface, m_support_material_interface_flow); } else { // base_areas - filler_support->spacing = support_flow.spacing(); Flow flow = (layer_id == 0 && m_raft_layers == 0) ? m_object->print()->brim_flow() : support_flow; - if (layer_id>0 && area_group.dist_to_top < 10 && !with_infill && m_object_config->support_style!=smsTreeHybrid) { + bool need_infill = with_infill; + if(m_object_config->support_base_pattern==smpDefault) + need_infill &= area_group.need_infill; + if (layer_id>0 && area_group.dist_to_top < 10 && !need_infill && support_style!=smsTreeHybrid) { if (area_group.dist_to_top < 5) // 1 wall at the top <5mm make_perimeter_and_inner_brim(ts_layer->support_fills.entities, poly, 1, flow, erSupportMaterial); else // at least 2 walls for range [5,10) make_perimeter_and_inner_brim(ts_layer->support_fills.entities, poly, std::max(wall_count, size_t(2)), flow, erSupportMaterial); + } + else if (layer_id > 0 && need_infill && m_support_params.base_fill_pattern != ipLightning) { + std::shared_ptr filler_support = std::shared_ptr(Fill::new_from_type(m_support_params.base_fill_pattern)); + filler_support->set_bounding_box(bbox_object); + filler_support->spacing = object_config.support_base_pattern_spacing.value * support_density;// constant spacing to align support infill lines + filler_support->angle = Geometry::deg2rad(object_config.support_angle.value); - } else if (layer_id > 0 && with_infill && m_support_params.base_fill_pattern != ipLightning) { - filler_support->angle = Geometry::deg2rad(object_config.support_angle.value); - - // allow infill-only mode if support is thick enough - if (offset(poly, -scale_(support_spacing * 1.5)).empty() == false) { - make_perimeter_and_infill(ts_layer->support_fills.entities, *m_object->print(), poly, wall_count, flow, erSupportMaterial, - filler_support.get(), support_density); - } else { // otherwise must draw 1 wall - make_perimeter_and_infill(ts_layer->support_fills.entities, *m_object->print(), poly, std::max(size_t(1), wall_count), flow, - erSupportMaterial, filler_support.get(), support_density); - } + // allow infill-only mode if support is thick enough (so min_wall_count is 0); + // otherwise must draw 1 wall + size_t min_wall_count = offset(poly, -scale_(support_spacing * 1.5)).empty() ? 1 : 0; + make_perimeter_and_infill(ts_layer->support_fills.entities, *m_object->print(), poly, std::max(min_wall_count, wall_count), flow, + erSupportMaterial, filler_support.get(), support_density); } else { make_perimeter_and_inner_brim(ts_layer->support_fills.entities, poly, @@ -1963,7 +1904,7 @@ if (!m_object->config().tree_support_adaptive_layer_height) profiler.stage_finish(STAGE_GENERATE_TOOLPATHS); profiler.stage_finish(STAGE_total); - BOOST_LOG_TRIVIAL(debug) << "tree support time " << profiler.report(); + BOOST_LOG_TRIVIAL(info) << "tree support time " << profiler.report(); } coordf_t TreeSupport::calc_branch_radius(coordf_t base_radius, size_t layers_to_top, size_t tip_layers, double diameter_angle_scale_factor) @@ -2065,7 +2006,6 @@ void TreeSupport::draw_circles(const std::vector>& contact_no const coordf_t layer_height = config.layer_height.value; const size_t top_interface_layers = config.support_interface_top_layers.value; const size_t bottom_interface_layers = config.support_interface_bottom_layers.value; - const size_t tip_layers = branch_radius / layer_height; //The number of layers to be shrinking the circle to create a tip. This produces a 45 degree angle. const double diameter_angle_scale_factor = tan(tree_support_branch_diameter_angle * M_PI / 180.);// * layer_height / branch_radius; //Scale factor per layer to produce the desired angle. const coordf_t line_width = config.support_line_width; const coordf_t line_width_scaled = scale_(line_width); @@ -2075,9 +2015,6 @@ void TreeSupport::draw_circles(const std::vector>& contact_no const size_t wall_count = config.tree_support_wall_count.value; const PrintObjectConfig& object_config = m_object->config(); - auto m_support_material_flow = support_material_flow(m_object, float(m_slicing_params.layer_height)); - coordf_t support_spacing = object_config.support_base_pattern_spacing.value + m_support_material_flow.spacing(); - coordf_t support_density = std::min(1., m_support_material_flow.spacing() / support_spacing); BOOST_LOG_TRIVIAL(info) << "draw_circles for object: " << m_object->model_object()->name; // coconut: previously std::unordered_map in m_collision_cache is not multi-thread safe which may cause programs stuck, here we change to tbb::concurrent_unordered_map @@ -2116,6 +2053,8 @@ void TreeSupport::draw_circles(const std::vector>& contact_no coordf_t max_layers_above_base = 0; coordf_t max_layers_above_roof = 0; coordf_t max_layers_above_roof1 = 0; + bool has_polygon_node = false; + bool has_circle_node = false; BOOST_LOG_TRIVIAL(debug) << "circles at layer " << layer_nr << " contact nodes size=" << contact_nodes[layer_nr].size(); //Draw the support areas and add the roofs appropriately to the support roof instead of normal areas. @@ -2136,6 +2075,8 @@ void TreeSupport::draw_circles(const std::vector>& contact_no else { area = offset_ex({ *node.overhang }, scale_(m_ts_data->m_xy_distance)); } + if (node.type == ePolygon) + has_polygon_node = true; } else { Polygon circle; @@ -2180,6 +2121,7 @@ void TreeSupport::draw_circles(const std::vector>& contact_no } append(area, overhang_expanded); } + has_circle_node = true; } if (node.distance_to_top < 0) @@ -2222,11 +2164,15 @@ void TreeSupport::draw_circles(const std::vector>& contact_no // avoid object auto avoid_region_interface = m_ts_data->get_collision(m_ts_data->m_xy_distance, layer_nr); - //roof_areas = std::move(diff_ex(roof_areas, avoid_region_interface)); - //roof_1st_layer = std::move(diff_ex(roof_1st_layer, avoid_region_interface)); - roof_areas = avoid_object_remove_extra_small_parts(roof_areas, avoid_region_interface); + if (has_circle_node) { + roof_areas = avoid_object_remove_extra_small_parts(roof_areas, avoid_region_interface); + roof_1st_layer = avoid_object_remove_extra_small_parts(roof_1st_layer, avoid_region_interface); + } + else { + roof_areas = std::move(diff_ex(roof_areas, avoid_region_interface)); + roof_1st_layer = std::move(diff_ex(roof_1st_layer, avoid_region_interface)); + } roof_areas = intersection_ex(roof_areas, m_machine_border); - roof_1st_layer = avoid_object_remove_extra_small_parts(roof_1st_layer, avoid_region_interface); // roof_1st_layer and roof_areas may intersect, so need to subtract roof_areas from roof_1st_layer roof_1st_layer = std::move(diff_ex(roof_1st_layer, roof_areas)); @@ -2243,7 +2189,7 @@ void TreeSupport::draw_circles(const std::vector>& contact_no if (SQUARE_SUPPORT) { // simplify support contours ExPolygons base_areas_simplified; - for (auto &area : base_areas) { area.simplify(scale_(line_width / 2), &base_areas_simplified, SimplifyMethodDP); } + for (auto &area : base_areas) { area.simplify(scale_(line_width / 2), &base_areas_simplified); } base_areas = std::move(base_areas_simplified); } //Subtract support floors. We can only compute floor_areas here instead of with roof_areas, @@ -2273,7 +2219,10 @@ void TreeSupport::draw_circles(const std::vector>& contact_no } } auto &area_groups = ts_layer->area_groups; - for (auto &area : ts_layer->base_areas) area_groups.emplace_back(&area, SupportLayer::BaseType, max_layers_above_base); + for (auto& area : ts_layer->base_areas) { + area_groups.emplace_back(&area, SupportLayer::BaseType, max_layers_above_base); + area_groups.back().need_infill = has_polygon_node; + } for (auto &area : ts_layer->roof_areas) area_groups.emplace_back(&area, SupportLayer::RoofType, max_layers_above_roof); for (auto &area : ts_layer->floor_areas) area_groups.emplace_back(&area, SupportLayer::FloorType, 10000); for (auto &area : ts_layer->roof_1st_layer) area_groups.emplace_back(&area, SupportLayer::Roof1stLayer, max_layers_above_roof1); @@ -2315,14 +2264,12 @@ void TreeSupport::draw_circles(const std::vector>& contact_no for (layer_nr_lower; layer_nr_lower >= 0; layer_nr_lower--) { if (!m_object->get_support_layer(layer_nr_lower + m_raft_layers)->area_groups.empty()) break; } + if (layer_nr_lower <= 0) continue; + SupportLayer* lower_layer = m_object->get_support_layer(layer_nr_lower + m_raft_layers); - ExPolygons& base_areas_lower = m_object->get_support_layer(layer_nr_lower + m_raft_layers)->base_areas; + ExPolygons& base_areas_lower = lower_layer->base_areas; ExPolygons overhang; - - if (layer_nr_lower == 0) - continue; - if (global_lightning_infill) { //search overhangs globally @@ -2336,7 +2283,8 @@ void TreeSupport::draw_circles(const std::vector>& contact_no { Polygon rev_hole = hole; rev_hole.make_counter_clockwise(); - ExPolygons ex_hole = to_expolygons(ExPolygon(rev_hole)); + ExPolygons ex_hole; + ex_hole.emplace_back(std::move(ExPolygon(rev_hole))); for (auto& other_area : base_areas) //if (&other_area != &base_area) ex_hole = std::move(diff_ex(ex_hole, other_area)); @@ -2346,7 +2294,7 @@ void TreeSupport::draw_circles(const std::vector>& contact_no } overhangs.emplace_back(to_polygons(overhang)); - contours.emplace_back(to_polygons(base_areas_lower)); //cant guarantee for 100% success probability, infill fails sometimes + contours.emplace_back(to_polygons(base_areas_lower)); printZ_to_lightninglayer[lower_layer->print_z] = overhangs.size() - 1; #ifdef SUPPORT_TREE_DEBUG_TO_SVG @@ -2354,6 +2302,10 @@ void TreeSupport::draw_circles(const std::vector>& contact_no #endif } + + auto m_support_material_flow = support_material_flow(m_object, m_slicing_params.layer_height); + coordf_t support_spacing = object_config.support_base_pattern_spacing.value + m_support_material_flow.spacing(); + coordf_t support_density = std::min(1., m_support_material_flow.spacing() / support_spacing * 2); // for lightning infill the density is defined differently, so need to double it generator = std::make_unique(m_object, contours, overhangs, []() {}, support_density); } @@ -2486,21 +2438,6 @@ void TreeSupport::draw_circles(const std::vector>& contact_no char fname[10]; sprintf(fname, "%d_%.2f", layer_nr, ts_layer->print_z); draw_contours_and_nodes_to_svg("", base_areas, roof_areas, roof_1st_layer, {}, {}, get_svg_filename(fname, "circles"), {"base", "roof", "roof1st"}); } - - // export layer & print_z log - std::ofstream draw_circles_layer_out; - draw_circles_layer_out.open("./SVG/layer_heights_draw_circles.txt"); - if (draw_circles_layer_out.is_open()) { - for (int layer_nr = m_object->layer_count() - 1; layer_nr > 0; layer_nr--) { - SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); - ExPolygons& base_areas = ts_layer->base_areas; - ExPolygons& roof_areas = ts_layer->roof_areas; - ExPolygons& roof_1st_layer = ts_layer->roof_1st_layer; - ExPolygons& floor_areas = ts_layer->floor_areas; - if (base_areas.empty() && roof_areas.empty() && roof_1st_layer.empty()) continue; - draw_circles_layer_out << layer_nr << " " << ts_layer->print_z << " " << ts_layer->height << std::endl; - } - } #endif // SUPPORT_TREE_DEBUG_TO_SVG SupportLayerPtrs& ts_layers = m_object->support_layers(); @@ -2561,39 +2498,48 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) m_spanning_trees.resize(contact_nodes.size()); //m_mst_line_x_layer_contour_caches.resize(contact_nodes.size()); - if (!is_slim) + if (0) {// get outlines below and avoidance area using tbb - //m_object->print()->set_status(59, "Support: preparing avoidance regions "); + // This part only takes very little time, so we disable it. + typedef std::chrono::high_resolution_clock clock_; + typedef std::chrono::duration > second_; + std::chrono::time_point t0{ clock_::now() }; + // get all the possible radiis std::vector > all_layer_radius(m_highest_overhang_layer+1); std::vector> all_layer_node_dist(m_highest_overhang_layer + 1); for (size_t layer_nr = m_highest_overhang_layer; layer_nr > 0; layer_nr--) { + if (layer_heights[layer_nr].height < EPSILON) continue; auto& layer_radius = all_layer_radius[layer_nr]; auto& layer_node_dist = all_layer_node_dist[layer_nr]; for (Node *p_node : contact_nodes[layer_nr]) { layer_node_dist.emplace(p_node->dist_mm_to_top); } size_t layer_nr_next = layer_heights[layer_nr].next_layer_nr; - if (layer_nr < m_highest_overhang_layer && layer_heights[layer_nr].height>0) { - for (auto node_dist : all_layer_node_dist[layer_nr_next]) - layer_node_dist.emplace(node_dist + layer_heights[layer_nr].height); + if (layer_nr_next <= m_highest_overhang_layer && layer_nr_next>0) { + for (auto node_dist : layer_node_dist) + all_layer_node_dist[layer_nr_next].emplace(node_dist + layer_heights[layer_nr].height); } for (auto node_dist : layer_node_dist) { layer_radius.emplace(calc_branch_radius(branch_radius, node_dist, diameter_angle_scale_factor)); } } // parallel pre-compute avoidance - tbb::parallel_for(tbb::blocked_range(1, m_highest_overhang_layer), [&](const tbb::blocked_range &range) { - for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++) { + //tbb::parallel_for(tbb::blocked_range(1, m_highest_overhang_layer), [&](const tbb::blocked_range &range) { + //for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++) { + for (size_t layer_nr = 0; layer_nr < all_layer_radius.size(); layer_nr++) { + BOOST_LOG_TRIVIAL(debug) << "pre calculate_avoidance layer=" << layer_nr; for (auto node_radius : all_layer_radius[layer_nr]) { m_ts_data->get_avoidance(0, layer_nr); m_ts_data->get_avoidance(node_radius, layer_nr); } } - }); + //}); - BOOST_LOG_TRIVIAL(debug) << "before m_avoidance_cache.size()=" << m_ts_data->m_avoidance_cache.size(); + double duration{ std::chrono::duration_cast(clock_::now() - t0).count() }; + BOOST_LOG_TRIVIAL(debug) << "before m_avoidance_cache.size()=" << m_ts_data->m_avoidance_cache.size() + << ", takes " << duration << " secs."; } for (size_t layer_nr = contact_nodes.size() - 1; layer_nr > 0; layer_nr--) // Skip layer 0, since we can't drop down the vertices there. @@ -2604,7 +2550,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) auto& layer_contact_nodes = contact_nodes[layer_nr]; if (layer_contact_nodes.empty()) continue; - + int layer_nr_next = layer_heights[layer_nr].next_layer_nr; coordf_t print_z_next = layer_heights[layer_nr_next].print_z; coordf_t height_next = layer_heights[layer_nr_next].height; @@ -2732,7 +2678,6 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) } const std::vector& neighbours = mst.adjacent_nodes(node.position); if (node.type == ePolygon) { - #if 1 // Remove all neighbours that are completely inside the polygon and merge them into this node. for (const Point &neighbour : neighbours) { Node * neighbour_node = nodes_per_part[group_index][neighbour]; @@ -2750,7 +2695,6 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) to_delete.insert(neighbour_node); } } - #endif } else if (neighbours.size() == 1 && vsize2_with_unscale(neighbours[0] - node.position) < max_move_distance2 && mst.adjacent_nodes(neighbours[0]).size() == 1 && nodes_per_part[group_index][neighbours[0]]->type!=ePolygon) // We have just two nodes left, and they're very close, and the only neighbor is not ePolygon @@ -2769,25 +2713,23 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) } Node* neighbour = nodes_per_part[group_index][neighbours[0]]; - size_t new_distance_to_top = std::max(node.distance_to_top, neighbour->distance_to_top) + 1; - size_t new_support_roof_layers_below = std::max(node.support_roof_layers_below, neighbour->support_roof_layers_below) - 1; - double new_dist_mm_to_top = std::max(node.dist_mm_to_top + node.height, neighbour->dist_mm_to_top+neighbour->height); - Node * parent; + Node* node_; if (p_node->parent && neighbour->parent) - parent = ((node.dist_mm_to_top >= neighbour->dist_mm_to_top && p_node->parent) ? p_node : neighbour)->parent; - else if (p_node->parent) - parent = p_node->parent; + node_ = (node.dist_mm_to_top >= neighbour->dist_mm_to_top && p_node->parent) ? p_node : neighbour; else - parent = neighbour->parent; - + node_ = p_node->parent ? p_node : neighbour; const bool to_buildplate = !is_inside_ex(m_ts_data->get_avoidance(0, layer_nr_next), next_position); - Node * next_node = new Node(next_position, new_distance_to_top, layer_nr_next, new_support_roof_layers_below, to_buildplate, p_node, - print_z_next, height_next, new_dist_mm_to_top); + Node * next_node = new Node(next_position, node_->distance_to_top + 1, layer_nr_next, node_->support_roof_layers_below-1, to_buildplate, node_->parent, + print_z_next, height_next); next_node->movement = next_position - node.position; get_max_move_dist(next_node); next_node->is_merged = true; contact_nodes[layer_nr_next].push_back(next_node); + // make sure the trees are all connected + if (node.parent) node.parent->child = next_node; + if (neighbour->parent) neighbour->parent->child = next_node; + // Make sure the next pass doesn't drop down either of these (since that already happened). node.merged_neighbours.push_front(neighbour); to_delete.insert(neighbour); @@ -2839,7 +2781,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) if (group_index > 0 && is_inside_ex(m_ts_data->get_collision(m_ts_data->m_xy_distance, layer_nr), node.position)) { const coordf_t branch_radius_node = calc_branch_radius(branch_radius, node.dist_mm_to_top, diameter_angle_scale_factor); - Point to_outside = projection_onto_ex(m_ts_data->get_collision(m_ts_data->m_xy_distance, layer_nr), node.position); + Point to_outside = projection_onto(m_ts_data->get_collision(m_ts_data->m_xy_distance, layer_nr), node.position); double dist2_to_outside = vsize2_with_unscale(node.position - to_outside); if (dist2_to_outside >= branch_radius_node * branch_radius_node) //Too far inside. { @@ -2894,13 +2836,13 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) if (is_line_cut_by_contour(node.position, neighbour)) continue; - if (/*is_slim*/1) + if (!is_strong) sum_direction += direction * (1 / dist2_to_neighbor); else - sum_direction += direction; + sum_direction += direction; } - if (/*is_slim*/1) + if (!is_strong) move_to_neighbor_center = sum_direction; else { if (vsize2_with_unscale(sum_direction) <= max_move_distance2) { @@ -2920,7 +2862,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) #endif auto avoid_layer = m_ts_data->get_avoidance(branch_radius_node, layer_nr_next); - Point to_outside = projection_onto_ex(avoid_layer, node.position); + Point to_outside = projection_onto(avoid_layer, node.position); Point direction_to_outer = to_outside - node.position; double dist2_to_outer = vsize2_with_unscale(direction_to_outer); // don't move if @@ -2942,7 +2884,7 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) } // move to the averaged direction of neighbor center and contour edge if they are roughly same direction Point movement; - if (/*is_slim*/1) + if (!is_strong) movement = move_to_neighbor_center*2 + (dist2_to_outer > EPSILON ? direction_to_outer * (1 / dist2_to_outer) : Point(0, 0)); else { if (movement.dot(move_to_neighbor_center) >= 0.2 || move_to_neighbor_center == Point(0, 0)) @@ -3044,90 +2986,98 @@ void TreeSupport::drop_nodes(std::vector>& contact_nodes) delete node; } to_free_node_set.clear(); - - // Merge empty contact_nodes layers - - -#ifdef SUPPORT_TREE_DEBUG_TO_SVG - // export all print_z and layer height into .txt - std::ofstream layer_heights_out; - layer_heights_out.open("./SVG/layer_heights_drop_nodes.txt"); - //layer_heights_out.open("layer_heights_out.txt"); - if (layer_heights_out.is_open()) { - for (int i = 0; i < layer_heights.size(); i++) { - if (contact_nodes[i].empty()) { - layer_heights_out << 0 << " " << 0 << std::endl; - } - else { - layer_heights_out << contact_nodes[i][0]->print_z << " " << contact_nodes[i][0]->height << std::endl; - } - } - layer_heights_out.close(); - } -#endif } void TreeSupport::smooth_nodes(std::vector> &contact_nodes) { - std::map is_processed; for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) { std::vector &curr_layer_nodes = contact_nodes[layer_nr]; if (curr_layer_nodes.empty()) continue; for (Node *node : curr_layer_nodes) { - is_processed.emplace(node, false); + node->is_processed = false; if (layer_nr == 0) node->is_merged = true; // nodes on plate are also merged nodes } } - for (int layer_nr = contact_nodes.size()-1; layer_nr >=0 ; layer_nr--) { + for (int layer_nr = 0; layer_nr< contact_nodes.size(); layer_nr++) { std::vector &curr_layer_nodes = contact_nodes[layer_nr]; if (curr_layer_nodes.empty()) continue; for (Node *node : curr_layer_nodes) { - if (!is_processed[node]) { + if (!node->is_processed) { std::vector pts; std::vector branch; Node * p_node = node; - // add head for second path from the merged node if there are multiple ones - if (!node->is_merged && node->parent) { - pts.push_back(p_node->parent->position); - branch.push_back(p_node->parent); + // add a fixed head + if (node->child) { + pts.push_back(p_node->child->position); + branch.push_back(p_node->child); } do { pts.push_back(p_node->position); - //is_processed[p_node] = true; branch.push_back(p_node); - p_node = p_node->child; - } while (p_node && !p_node->is_merged && !is_processed[p_node]); - // TODO is it necessary to add tail also? - if (p_node && p_node->is_merged) { - pts.push_back(p_node->position); - branch.push_back(p_node); - } + p_node = p_node->parent; + } while (p_node && !p_node->is_processed); if (pts.size() < 3) continue; std::vector pts1 = pts; // TODO here we assume layer height gap is constant. If not true, need to consider height jump - // TODO it seems the smooth iterations can't be larger than 1, otherwise some nodes will fly away - for (size_t k = 0; k < 1; k++) { + const int iterations = 100; + for (size_t k = 0; k < iterations; k++) { for (size_t i = 1; i < pts.size() - 1; i++) { size_t i2 = i >= 2 ? i - 2 : 0; size_t i3 = i < pts.size() - 2 ? i + 2 : pts.size() - 1; Point pt = (pts[i2] + pts[i - 1] + pts[i] + pts[i + 1] + pts[i3]) / 5; pts1[i] = pt; + if (k == iterations - 1) { + branch[i]->position = pt; + branch[i]->movement = (pts[i + 1] - pts[i - 1]) / 2; + branch[i]->is_processed = true; + } } - std::swap(pts, pts1); - } - for (size_t i = 1; i < pts.size() - 1; i++) { - if (!is_processed[branch[i]]) { - // do not move if the new position is too far away - if (vsize2_with_unscale(branch[i]->position - pts[i]) < SQ(m_support_params.support_extrusion_width * 2)) { branch[i]->position = pts[i]; } - branch[i]->movement = branch[i]->parent ? (branch[i]->position - branch[i]->parent->position) : Point(0, 0); - is_processed[branch[i]] = true; - } + if (k < iterations - 1) + std::swap(pts, pts1); } } } } +#ifdef SUPPORT_TREE_DEBUG_TO_SVG + // save tree structure for viewing in python + struct TreeNode { + Vec3f pos; + std::vector children; // index of children in the storing vector + TreeNode(Point pt, float z) { + pos = { float(unscale_(pt.x())),float(unscale_(pt.y())),z }; + } + }; + std::vector tree_nodes; + std::map ptr2idx; + std::map idx2ptr; + for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) { + std::vector& curr_layer_nodes = contact_nodes[layer_nr]; + for (Node* node : curr_layer_nodes) { + ptr2idx.emplace(node, tree_nodes.size()); + idx2ptr.emplace(tree_nodes.size(), node); + tree_nodes.emplace_back(node->position, node->print_z); + } + } + for (size_t i = 0; i < tree_nodes.size(); i++) { + TreeNode& tree_node = tree_nodes[i]; + Node* p_node = idx2ptr[i]; + if (p_node->child) + tree_node.children.push_back(ptr2idx[p_node->child]); + } + nlohmann::json jj; + for (size_t i = 0; i < tree_nodes.size(); i++) { + nlohmann::json j; + j["pos"] = tree_nodes[i].pos; + j["children"] = tree_nodes[i].children; + jj.push_back(j); + } + + std::ofstream ofs("tree_nodes.json"); + ofs << jj.dump(); + ofs.close(); +#endif } void TreeSupport::adjust_layer_heights(std::vector>& contact_nodes) @@ -3301,31 +3251,23 @@ std::vector TreeSupport::plan_layer_heights(std::vector= 0; i--) { - if (layer_heights[i].height < EPSILON) continue; - for (int j = i - 1; j >= 0; j--) { + // fill in next_layer_nr + int i = layer_heights.size() - 1, j = i; + for (; j >= 0; i = j) { + if (layer_heights[i].height < EPSILON) { + j--; + continue; + } + for (j = i - 1; j >= 0; j--) { if (layer_heights[j].height > EPSILON) { layer_heights[i].next_layer_nr = j; break; } } + BOOST_LOG_TRIVIAL(info) << "plan_layer_heights print_z, height, layer_nr->next_layer_nr: " << layer_heights[i].print_z << " " << layer_heights[i].height << " " + << i << "->" << layer_heights[i].next_layer_nr << std::endl; } -#ifdef SUPPORT_TREE_DEBUG_TO_SVG - // check bounds - if (1) - { - std::ofstream bounds_out; - bounds_out.open("bounds.txt"); - if (bounds_out.is_open()) { - for (int i = 0; i < bounds.size(); i++) { - bounds_out << bounds[i] << std::endl; - } - } - } -#endif - for (int i = 0; i < layer_heights.size(); i++) { BOOST_LOG_TRIVIAL(info) << "plan_layer_heights print_z, height: "<< layer_heights[i].print_z << " " << layer_heights[i].height << std::endl; } - return layer_heights; } @@ -3370,7 +3312,7 @@ void TreeSupport::generate_contact_points(std::vector 0) support_roof_layers += 1; // BBS: add a normal support layer below interface (if we have interface) - coordf_t thresh_angle = config.support_threshold_angle.value < EPSILON ? 30.f : config.support_threshold_angle.value; + coordf_t thresh_angle = std::min(89.f, config.support_threshold_angle.value < EPSILON ? 30.f : config.support_threshold_angle.value); coordf_t half_overhang_distance = scale_(tan(thresh_angle * M_PI / 180.0) * layer_height / 2); // fix bug of generating support for very thin objects @@ -3397,7 +3339,7 @@ void TreeSupport::generate_contact_points(std::vector m_support_params.thresh_big_overhang) { + if (support_style==smsTreeHybrid && overhang_part.area() > m_support_params.thresh_big_overhang) { Point candidate = overhang_bounds.center(); if (!overhang_part.contains(candidate)) move_inside_expoly(overhang_part, candidate); @@ -3571,11 +3513,11 @@ const ExPolygons& TreeSupportData::get_collision(coordf_t radius, size_t layer_n return collision; } -const ExPolygons& TreeSupportData::get_avoidance(coordf_t radius, size_t layer_nr) const +const ExPolygons& TreeSupportData::get_avoidance(coordf_t radius, size_t layer_nr, int recursions) const { profiler.tic(); radius = ceil_radius(radius); - RadiusLayerPair key{radius, layer_nr}; + RadiusLayerPair key{radius, layer_nr, recursions }; const auto it = m_avoidance_cache.find(key); const ExPolygons& avoidance = it != m_avoidance_cache.end() ? it->second : calculate_avoidance(key); @@ -3622,22 +3564,21 @@ coordf_t TreeSupportData::ceil_radius(coordf_t radius) const const ExPolygons& TreeSupportData::calculate_collision(const RadiusLayerPair& key) const { - const auto& radius = key.first; - const auto& layer_nr = key.second; + assert(key.layer_nr < m_layer_outlines.size()); - assert(layer_nr < m_layer_outlines.size()); - - ExPolygons collision_areas = std::move(offset_ex(m_layer_outlines[layer_nr], scale_(radius))); + ExPolygons collision_areas = std::move(offset_ex(m_layer_outlines[key.layer_nr], scale_(key.radius))); const auto ret = m_collision_cache.insert({ key, std::move(collision_areas) }); return ret.first->second; } const ExPolygons& TreeSupportData::calculate_avoidance(const RadiusLayerPair& key) const { - const auto& radius = key.first; - const auto& layer_nr = key.second; + const auto& radius = key.radius; + const auto& layer_nr = key.layer_nr; + BOOST_LOG_TRIVIAL(debug) << "calculate_avoidance on radius=" << radius << ", layer=" << layer_nr<<", recursion="<::iterator,bool> ret; - if (/*is_slim*/1) { + constexpr auto max_recursion_depth = 100; + if (key.recursions <= max_recursion_depth*2) { if (layer_nr == 0) { m_avoidance_cache[key] = get_collision(radius, 0); return m_avoidance_cache[key]; @@ -3648,19 +3589,17 @@ const ExPolygons& TreeSupportData::calculate_avoidance(const RadiusLayerPair& ke // below the current one exists and if not, forcing the calculation of that layer. This may cause another recursion // if the layer at 2N below the current one but we won't exceed our limit unless there are N*N uncalculated layers // below our current one. - constexpr auto max_recursion_depth = 100; size_t layer_nr_next = layer_nr; - for (int i = 0; i < max_recursion_depth && layer_nr_next>0; i++) { - layer_nr_next = layer_heights[layer_nr_next].next_layer_nr; - } + int layers_below; + for (layers_below = 0; layers_below < max_recursion_depth && layer_nr_next > 0; layers_below++) { layer_nr_next = layer_heights[layer_nr_next].next_layer_nr; } // Check if we would exceed the recursion limit by trying to process this layer - if (layer_nr >= max_recursion_depth && m_avoidance_cache.find({radius, layer_nr_next}) == m_avoidance_cache.end()) { + if (layers_below >= max_recursion_depth && m_avoidance_cache.find({radius, layer_nr_next}) == m_avoidance_cache.end()) { // Force the calculation of the layer `max_recursion_depth` below our current one, ignoring the result. - get_avoidance(radius, layer_nr_next); + get_avoidance(radius, layer_nr_next, key.recursions + 1); } layer_nr_next = layer_heights[layer_nr].next_layer_nr; - ExPolygons avoidance_areas = std::move(offset_ex(get_avoidance(radius, layer_nr_next), scale_(-m_max_move))); + ExPolygons avoidance_areas = std::move(offset_ex(get_avoidance(radius, layer_nr_next, key.recursions+1), scale_(-m_max_move))); const ExPolygons &collision = get_collision(radius, layer_nr); avoidance_areas.insert(avoidance_areas.end(), collision.begin(), collision.end()); avoidance_areas = std::move(union_ex(avoidance_areas)); diff --git a/src/libslic3r/TreeSupport.hpp b/src/libslic3r/TreeSupport.hpp index bb7d1ef470..169236ee7d 100644 --- a/src/libslic3r/TreeSupport.hpp +++ b/src/libslic3r/TreeSupport.hpp @@ -83,7 +83,7 @@ public: * \param layer The layer of interest * \return Polygons object */ - const ExPolygons& get_avoidance(coordf_t radius, size_t layer_idx) const; + const ExPolygons& get_avoidance(coordf_t radius, size_t layer_idx, int recursions=0) const; Polygons get_contours(size_t layer_nr) const; Polygons get_contours_with_holes(size_t layer_nr) const; @@ -94,11 +94,20 @@ private: /*! * \brief Convenience typedef for the keys to the caches */ - using RadiusLayerPair = std::pair; - + struct RadiusLayerPair { + coordf_t radius; + size_t layer_nr; + int recursions; + + }; + struct RadiusLayerPairEquality { + constexpr bool operator()(const RadiusLayerPair& _Left, const RadiusLayerPair& _Right) const { + return _Left.radius == _Right.radius && _Left.layer_nr == _Right.layer_nr; + } + }; struct RadiusLayerPairHash { size_t operator()(const RadiusLayerPair& elem) const { - return std::hash()(elem.first) ^ std::hash()(elem.second * 7919); + return std::hash()(elem.radius) ^ std::hash()(elem.layer_nr * 7919); } }; @@ -168,8 +177,8 @@ public: * coconut: previously stl::unordered_map is used which seems problematic with tbb::parallel_for. * So we change to tbb::concurrent_unordered_map */ - mutable tbb::concurrent_unordered_map m_collision_cache; - mutable tbb::concurrent_unordered_map m_avoidance_cache; + mutable tbb::concurrent_unordered_map m_collision_cache; + mutable tbb::concurrent_unordered_map m_avoidance_cache; friend TreeSupport; }; @@ -203,7 +212,7 @@ public: */ void generate(); - void detect_overhangs(); + void detect_overhangs(bool detect_first_sharp_tail_only=false); enum NodeType { eCircle, @@ -230,6 +239,7 @@ public: , height(0.0) {} + // when dist_mm_to_top_==0, new node's dist_mm_to_top=parent->dist_mm_to_top + parent->height; Node(const Point position, const int distance_to_top, const int obj_layer_nr, const int support_roof_layers_below, const bool to_buildplate, Node* parent, coordf_t print_z_, coordf_t height_, coordf_t dist_mm_to_top_=0) : distance_to_top(distance_to_top) @@ -249,6 +259,8 @@ public: if (dist_mm_to_top==0) dist_mm_to_top = parent->dist_mm_to_top + parent->height; parent->child = this; + for (auto& neighbor : parent->merged_neighbours) + neighbor->child = this; } } @@ -270,15 +282,15 @@ public: /*! * \brief The position of this node on the layer. */ - Point position; - - Point movement; // movement towards neighbor center or outline - mutable double radius = 0.0; - mutable double max_move_dist = 0.0; - NodeType type = eCircle; - bool is_merged = false; // this node is generated by merging upper nodes + Point position; + Point movement; // movement towards neighbor center or outline + mutable double radius = 0.0; + mutable double max_move_dist = 0.0; + NodeType type = eCircle; + bool is_merged = false; // this node is generated by merging upper nodes bool is_corner = false; - const ExPolygon* overhang = nullptr; // when type==ePolygon, set this value to get original overhang area + bool is_processed = false; + const ExPolygon *overhang = nullptr; // when type==ePolygon, set this value to get original overhang area /*! * \brief The direction of the skin lines above the tip of the branch. @@ -371,7 +383,9 @@ public: bool has_overhangs = false; bool has_sharp_tails = false; bool has_cantilever = false; + double max_cantilever_dist = 0; SupportType support_type; + SupportMaterialStyle support_style; std::unique_ptr generator; std::unordered_map printZ_to_lightninglayer; @@ -395,6 +409,7 @@ private: coordf_t MAX_BRANCH_RADIUS = 10.0; coordf_t MIN_BRANCH_RADIUS = 0.5; float tree_support_branch_diameter_angle = 5.0; + bool is_strong = false; bool is_slim = false; bool with_infill = false; diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 5fa3611192..b5d0d10883 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -984,6 +984,61 @@ indexed_triangle_set its_make_cone(double r, double h, double fa) return mesh; } +// Generates mesh for a frustum dowel centered about the origin, using the count of sectors +// Note: This function uses code for sphere generation, but for stackCount = 2; +indexed_triangle_set its_make_frustum_dowel(double radius, double h, int sectorCount) +{ + int stackCount = 2; + float sectorStep = float(2. * M_PI / sectorCount); + float stackStep = float(M_PI / stackCount); + + indexed_triangle_set mesh; + auto& vertices = mesh.vertices; + vertices.reserve((stackCount - 1) * sectorCount + 2); + for (int i = 0; i <= stackCount; ++i) { + // from pi/2 to -pi/2 + double stackAngle = 0.5 * M_PI - stackStep * i; + double xy = radius * cos(stackAngle); + double z = radius * sin(stackAngle); + if (i == 0 || i == stackCount) + vertices.emplace_back(Vec3f(float(xy), 0.f, float(h * sin(stackAngle)))); + else + for (int j = 0; j < sectorCount; ++j) { + // from 0 to 2pi + double sectorAngle = sectorStep * j + 0.25 * M_PI; + vertices.emplace_back(Vec3d(xy * std::cos(sectorAngle), xy * std::sin(sectorAngle), z).cast()); + } + } + + auto& facets = mesh.indices; + facets.reserve(2 * (stackCount - 1) * sectorCount); + for (int i = 0; i < stackCount; ++i) { + // Beginning of current stack. + int k1 = (i == 0) ? 0 : (1 + (i - 1) * sectorCount); + int k1_first = k1; + // Beginning of next stack. + int k2 = (i == 0) ? 1 : (k1 + sectorCount); + int k2_first = k2; + for (int j = 0; j < sectorCount; ++j) { + // 2 triangles per sector excluding first and last stacks + int k1_next = k1; + int k2_next = k2; + if (i != 0) { + k1_next = (j + 1 == sectorCount) ? k1_first : (k1 + 1); + facets.emplace_back(k1, k2, k1_next); + } + if (i + 1 != stackCount) { + k2_next = (j + 1 == sectorCount) ? k2_first : (k2 + 1); + facets.emplace_back(k1_next, k2, k2_next); + } + k1 = k1_next; + k2 = k2_next; + } + } + + return mesh; +} + indexed_triangle_set its_make_pyramid(float base, float height) { float a = base / 2.f; diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index 79aac12cc6..9afaddd4eb 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -337,6 +337,7 @@ indexed_triangle_set its_make_cube(double x, double y, double z); indexed_triangle_set its_make_prism(float width, float length, float height); indexed_triangle_set its_make_cylinder(double r, double h, double fa=(2*PI/360)); indexed_triangle_set its_make_cone(double r, double h, double fa=(2*PI/360)); +indexed_triangle_set its_make_frustum_dowel(double r, double h, int sectorCount); indexed_triangle_set its_make_pyramid(float base, float height); indexed_triangle_set its_make_sphere(double radius, double fa); diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp index e605cdcfe4..3fd0da6604 100644 --- a/src/libslic3r/TriangleMeshSlicer.cpp +++ b/src/libslic3r/TriangleMeshSlicer.cpp @@ -41,6 +41,16 @@ #endif namespace Slic3r { +const float epson = 1e-3; +bool is_equal(float lh, float rh) +{ + return abs(lh - rh) <= epson; +} + +bool is_less(float lh, float rh) +{ + return lh + epson < rh; +} class IntersectionReference { @@ -2007,11 +2017,12 @@ static void triangulate_slice( map_vertex_to_index.emplace_back(to_2d(its.vertices[i]), i); std::sort(map_vertex_to_index.begin(), map_vertex_to_index.end(), [](const std::pair &l, const std::pair &r) { - return l.first.x() < r.first.x() || - (l.first.x() == r.first.x() && (l.first.y() < r.first.y() || - (l.first.y() == r.first.y() && l.second < r.second))); }); + return is_less(l.first.x(), r.first.x()) || + (is_equal(l.first.x(), r.first.x()) && (is_less(l.first.y(), r.first.y()) || + (is_equal(l.first.y(), r.first.y()) && l.second < r.second))); }); // 2) Discover duplicate points on the slice. Remap duplicate vertices to a vertex with a lowest index. + // Remove denegerate triangles, if they happen to be created by merging duplicate vertices. { std::vector map_duplicate_vertex(int(its.vertices.size()) - num_original_vertices, -1); int i = 0; @@ -2024,7 +2035,7 @@ static void triangulate_slice( // map to itself map_duplicate_vertex[iidx - num_original_vertices] = iidx; int j = i; - for (++ j; j < int(map_vertex_to_index.size()) && ipos.x() == map_vertex_to_index[j].first.x() && ipos.y() == map_vertex_to_index[j].first.y(); ++ j) { + for (++ j; j < int(map_vertex_to_index.size()) && is_equal(ipos.x(), map_vertex_to_index[j].first.x()) && is_equal(ipos.y(), map_vertex_to_index[j].first.y()); ++ j) { const int jidx = map_vertex_to_index[j].second; assert(jidx >= num_original_vertices); if (jidx >= num_original_vertices) @@ -2034,10 +2045,20 @@ static void triangulate_slice( i = j; } map_vertex_to_index.erase(map_vertex_to_index.begin() + k, map_vertex_to_index.end()); - for (stl_triangle_vertex_indices &f : its.indices) - for (i = 0; i < 3; ++ i) - if (f(i) >= num_original_vertices) - f(i) = map_duplicate_vertex[f(i) - num_original_vertices]; + for (i = 0; i < int(its.indices.size());) { + stl_triangle_vertex_indices &f = its.indices[i]; + // Remap the newly added face vertices. + for (k = 0; k < 3; ++ k) + if (f(k) >= num_original_vertices) + f(k) = map_duplicate_vertex[f(k) - num_original_vertices]; + if (f(0) == f(1) || f(0) == f(2) || f(1) == f(2)) { + // Remove degenerate face. + f = its.indices.back(); + its.indices.pop_back(); + } else + // Keep the face. + ++ i; + } } if (triangulate) { @@ -2048,9 +2069,11 @@ static void triangulate_slice( for (size_t j = 0; j < 3; ++ j) { Vec3f v = triangles[i ++].cast(); auto it = lower_bound_by_predicate(map_vertex_to_index.begin(), map_vertex_to_index.end(), - [&v](const std::pair &l) { return l.first.x() < v.x() || (l.first.x() == v.x() && l.first.y() < v.y()); }); + [&v](const std::pair &l) { + return is_less(l.first.x(), v.x()) || (is_equal(l.first.x(), v.x()) && is_less(l.first.y(), v.y())); + }); int idx = -1; - if (it != map_vertex_to_index.end() && it->first.x() == v.x() && it->first.y() == v.y()) + if (it != map_vertex_to_index.end() && is_equal(it->first.x(), v.x()) && is_equal(it->first.y(), v.y())) idx = it->second; else { // Try to find the vertex in the list of newly added vertices. Those vertices are not matched on the cut and they shall be rare. diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index b59022f61d..f105983e60 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -36,6 +36,7 @@ #define CLI_3MF_NEW_MACHINE_NOT_SUPPORTED -16 #define CLI_PROCESS_NOT_COMPATIBLE -17 #define CLI_INVALID_VALUES_IN_3MF -18 +#define CLI_POSTPROCESS_NOT_SUPPORTED -19 #define CLI_NO_SUITABLE_OBJECTS -50 @@ -46,9 +47,12 @@ #define CLI_IMPORT_CACHE_NOT_FOUND -55 #define CLI_IMPORT_CACHE_DATA_CAN_NOT_USE -56 #define CLI_IMPORT_CACHE_LOAD_FAILED -57 +#define CLI_SLICING_TIME_EXCEEDS_LIMIT -58 +#define CLI_TRIANGLE_COUNT_EXCEEDS_LIMIT -59 +#define CLI_NO_SUITABLE_OBJECTS_AFTER_SKIP -60 - -#define CLI_SLICING_ERROR -100 +#define CLI_SLICING_ERROR -100 +#define CLI_GCODE_PATH_CONFLICTS -101 namespace boost { namespace filesystem { class directory_entry; }} diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index 2969a1d5f7..d42b85c505 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -37,6 +37,7 @@ #include #include #include + #include #endif #endif @@ -895,6 +896,8 @@ CopyFileResult copy_file(const std::string &from, const std::string &to, std::st goto __finished; } + FlushFileBuffers(handledst); + __finished: if (src_wstr) delete[] src_wstr; @@ -1149,7 +1152,16 @@ std::string get_process_name(int pid) while (auto q = strchr(p + 1, '/')) p = q; return p; #else - return {}; + char pathbuf[512] = {0}; + char proc_path[32] = "/proc/self/exe"; + if (pid != 0) { snprintf(proc_path, sizeof(proc_path), "/proc/%d/exe", pid); } + if (readlink(proc_path, pathbuf, sizeof(pathbuf)) < 0) { + perror(NULL); + return {}; + } + char *p = pathbuf; + while (auto q = strchr(p + 1, '/')) p = q; + return p; #endif } diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 2e8cd53043..675274a0bd 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -82,6 +82,8 @@ set(SLIC3R_GUI_SOURCES GUI/AuxiliaryDialog.hpp GUI/Auxiliary.cpp GUI/Auxiliary.hpp + GUI/Project.cpp + GUI/Project.hpp GUI/BackgroundSlicingProcess.cpp GUI/BackgroundSlicingProcess.hpp GUI/BitmapCache.cpp @@ -195,6 +197,8 @@ set(SLIC3R_GUI_SOURCES GUI/GUI_Factories.hpp GUI/GUI_ObjectList.cpp GUI/GUI_ObjectList.hpp + GUI/GUI_ObjectLayers.cpp + GUI/GUI_ObjectLayers.hpp GUI/GUI_AuxiliaryList.cpp GUI/GUI_AuxiliaryList.hpp GUI/GUI_ObjectSettings.cpp @@ -267,6 +271,8 @@ set(SLIC3R_GUI_SOURCES GUI/3DBed.hpp GUI/Camera.cpp GUI/Camera.hpp + GUI/CameraUtils.cpp + GUI/CameraUtils.hpp GUI/wxExtensions.cpp GUI/wxExtensions.hpp GUI/WipeTowerDialog.cpp diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 6887e459bd..6198bf31db 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -569,26 +569,6 @@ Transform3d GLVolume::world_matrix() const return m; } -//BBS: scaled_matrix -Transform3d GLVolume::world_matrix( float scale_factor) const -{ - //const Vec3d& volume_translation = m_volume_transformation.get_offset(); - //Vec3d scaling_factor = { scale_factor, scale_factor, scale_factor }; - Vec3d ofs2ass = m_offset_to_assembly * (GLVolume::explosion_ratio - 1.0); - Vec3d volofs2obj = m_volume_transformation.get_offset() * (GLVolume::explosion_ratio - 1.0); - - Transform3d volume_matrix = Geometry::assemble_transform( - m_volume_transformation.get_offset() + ofs2ass + volofs2obj, - m_volume_transformation.get_rotation(), - m_volume_transformation.get_scaling_factor() * scale_factor, - m_volume_transformation.get_mirror() - ); - Transform3d m = m_instance_transformation.get_matrix() * volume_matrix; - - //m.translation()(2) += m_sla_shift_z; - return m; -} - bool GLVolume::is_left_handed() const { const Vec3d &m1 = m_instance_transformation.get_mirror(); @@ -834,8 +814,6 @@ void GLVolume::render(bool with_outline) const fclose(file); } #endif - - Transform3d matrix = world_matrix(); render_body(); //BOOST_LOG_TRIVIAL(info) << boost::format(": %1%, outline render body, shader name %2%")%__LINE__ %shader->get_name(); @@ -887,7 +865,8 @@ void GLVolume::render(bool with_outline) const glsafe(::glPopMatrix()); glsafe(::glPushMatrix()); - matrix = world_matrix(scale); + Transform3d matrix = world_matrix(); + matrix.scale(scale); glsafe(::glMultMatrixd(matrix.data())); this->indexed_vertex_array.render(this->tverts_range, this->qverts_range); //BOOST_LOG_TRIVIAL(info) << boost::format(": %1%, outline render for body, shader name %2%")%__LINE__ %shader->get_name(); @@ -1065,7 +1044,8 @@ int GLVolumeCollection::load_object_volume( int instance_idx, const std::string &color_by, bool opengl_initialized, - bool in_assemble_view) + bool in_assemble_view, + bool use_loaded_id) { const ModelVolume *model_volume = model_object->volumes[volume_idx]; const int extruder_id = model_volume->extruder_id(); @@ -1100,6 +1080,11 @@ int GLVolumeCollection::load_object_volume( else v.set_instance_transformation(instance->get_transformation()); v.set_volume_transformation(model_volume->get_transformation()); + //use object's instance id + if (use_loaded_id && (instance->loaded_id > 0)) + v.model_object_ID = instance->loaded_id; + else + v.model_object_ID = instance->id().id; return int(this->volumes.size() - 1); } @@ -1287,14 +1272,13 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab #endif // ENABLE_MODIFIERS_ALWAYS_TRANSPARENT // render sinking contours of non-hovered volumes - //BBS: remove sinking logic - /*if (m_show_sinking_contours) + if (m_show_sinking_contours) if (volume.first->is_sinking() && !volume.first->is_below_printbed() && volume.first->hover == GLVolume::HS_None && !volume.first->force_sinking_contours) { shader->stop_using(); volume.first->render_sinking_contours(); shader->start_using(); - }*/ + } glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); @@ -1350,8 +1334,7 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); } - //BBS: remove sinking logic - /*if (m_show_sinking_contours) { + if (m_show_sinking_contours) { for (GLVolumeWithIdAndZ& volume : to_render) { // render sinking contours of hovered/displaced volumes if (volume.first->is_sinking() && !volume.first->is_below_printbed() && @@ -1363,7 +1346,7 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab shader->start_using(); } } - }*/ + } if (disable_cullface) glsafe(::glEnable(GL_CULL_FACE)); diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 86c93a47a7..c61a4eb284 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -357,6 +357,8 @@ public: // An ID containing the extruder ID (used to select color). int extruder_id; + size_t model_object_ID{0}; + // Various boolean flags. struct { // Is this object selected? @@ -501,9 +503,6 @@ public: Transform3d world_matrix() const; bool is_left_handed() const; - //BBS: world_matrix with scale factor - Transform3d world_matrix(float scale_factor) const; - const BoundingBoxf3& transformed_bounding_box() const; // non-caching variant BoundingBoxf3 transformed_convex_hull_bounding_box(const Transform3d &trafo) const; @@ -637,7 +636,8 @@ public: int instance_idx, const std::string &color_by, bool opengl_initialized, - bool in_assemble_view = false); + bool in_assemble_view = false, + bool use_loaded_id = false); // Load SLA auxiliary GLVolumes (for support trees or pad). void load_object_auxiliary( diff --git a/src/slic3r/GUI/AMSMaterialsSetting.cpp b/src/slic3r/GUI/AMSMaterialsSetting.cpp index 7a7eba8674..743c454a6a 100644 --- a/src/slic3r/GUI/AMSMaterialsSetting.cpp +++ b/src/slic3r/GUI/AMSMaterialsSetting.cpp @@ -4,12 +4,15 @@ #include "GUI_App.hpp" #include "libslic3r/Preset.hpp" #include "I18N.hpp" +#include namespace Slic3r { namespace GUI { -static bool show_flag; + +wxDEFINE_EVENT(EVT_SELECTED_COLOR, wxCommandEvent); AMSMaterialsSetting::AMSMaterialsSetting(wxWindow *parent, wxWindowID id) : DPIDialog(parent, id, _L("AMS Materials Setting"), wxDefaultPosition, wxDefaultSize, wxBORDER_NONE) + , m_color_picker_popup(ColorPickerPopup(this)) { create(); wxGetApp().UpdateDlgDarkUI(this); @@ -39,10 +42,18 @@ void AMSMaterialsSetting::create() m_button_confirm->SetCornerRadius(FromDIP(12)); m_button_confirm->Bind(wxEVT_BUTTON, &AMSMaterialsSetting::on_select_ok, this); - m_button_close = new Button(this, _L("Close")); - m_btn_bg_gray = StateColor(std::pair(wxColour(206, 206, 206), StateColor::Pressed), std::pair(*wxWHITE, StateColor::Focused), + m_button_reset = new Button(this, _L("Reset")); + m_btn_bg_gray = StateColor(std::pair(wxColour(206, 206, 206), StateColor::Pressed), std::pair(*wxWHITE, StateColor::Focused), std::pair(wxColour(238, 238, 238), StateColor::Hovered), - std::pair(*wxWHITE, StateColor::Normal)); + std::pair(*wxWHITE, StateColor::Normal)); + m_button_reset->SetBackgroundColor(m_btn_bg_gray); + m_button_reset->SetBorderColor(AMS_MATERIALS_SETTING_GREY900); + m_button_reset->SetTextColor(AMS_MATERIALS_SETTING_GREY900); + m_button_reset->SetMinSize(AMS_MATERIALS_SETTING_BUTTON_SIZE); + m_button_reset->SetCornerRadius(FromDIP(12)); + m_button_reset->Bind(wxEVT_BUTTON, &AMSMaterialsSetting::on_select_reset, this); + + m_button_close = new Button(this, _L("Close")); m_button_close->SetBackgroundColor(m_btn_bg_gray); m_button_close->SetBorderColor(AMS_MATERIALS_SETTING_GREY900); m_button_close->SetTextColor(AMS_MATERIALS_SETTING_GREY900); @@ -51,6 +62,7 @@ void AMSMaterialsSetting::create() m_button_close->Bind(wxEVT_BUTTON, &AMSMaterialsSetting::on_select_close, this); m_sizer_button->Add(m_button_confirm, 0, wxALIGN_CENTER | wxRIGHT, FromDIP(20)); + m_sizer_button->Add(m_button_reset, 0, wxALIGN_CENTER | wxRIGHT, FromDIP(20)); m_sizer_button->Add(m_button_close, 0, wxALIGN_CENTER, 0); m_sizer_main->Add(m_panel_normal, 0, wxALL, FromDIP(2)); @@ -96,6 +108,7 @@ void AMSMaterialsSetting::create() }); Bind(wxEVT_PAINT, &AMSMaterialsSetting::paintEvent, this); + Bind(EVT_SELECTED_COLOR, &AMSMaterialsSetting::on_picker_color, this); m_comboBox_filament->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(AMSMaterialsSetting::on_select_filament), NULL, this); } @@ -141,17 +154,12 @@ void AMSMaterialsSetting::create_panel_normal(wxWindow* parent) m_sizer_colour->Add(0, 0, 0, wxEXPAND, 0); - m_clrData = new wxColourData(); - m_clrData->SetChooseFull(true); - m_clrData->SetChooseAlpha(false); + m_clr_picker = new ColorPicker(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize); + m_clr_picker->set_show_full(true); + m_clr_picker->SetBackgroundColour(*wxWHITE); - m_clr_picker = new Button(parent, wxEmptyString, wxEmptyString, wxBU_AUTODRAW); - m_clr_picker->SetCanFocus(false); - m_clr_picker->SetSize(FromDIP(50), FromDIP(25)); - m_clr_picker->SetMinSize(wxSize(FromDIP(50), FromDIP(25))); - m_clr_picker->SetCornerRadius(FromDIP(6)); - m_clr_picker->SetBorderColor(wxColour(172, 172, 172)); - m_clr_picker->Bind(wxEVT_BUTTON, &AMSMaterialsSetting::on_clr_picker, this); + + m_clr_picker->Bind(wxEVT_LEFT_DOWN, &AMSMaterialsSetting::on_clr_picker, this); m_sizer_colour->Add(m_clr_picker, 0, 0, 0); wxBoxSizer* m_sizer_temperature = new wxBoxSizer(wxHORIZONTAL); @@ -260,8 +268,9 @@ void AMSMaterialsSetting::create_panel_kn(wxWindow* parent) { auto sizer = new wxBoxSizer(wxVERTICAL); // title - auto ratio_text = new wxStaticText(parent, wxID_ANY, _L("Factors of dynamic flow cali")); - ratio_text->SetFont(Label::Head_14); + m_ratio_text = new wxStaticText(parent, wxID_ANY, _L("Factors of dynamic flow cali")); + m_ratio_text->SetForegroundColour(wxColour(50, 58, 61)); + m_ratio_text->SetFont(Label::Head_14); auto kn_val_sizer = new wxFlexGridSizer(0, 2, 0, 0); kn_val_sizer->SetFlexibleDirection(wxBOTH); @@ -295,7 +304,7 @@ void AMSMaterialsSetting::create_panel_kn(wxWindow* parent) m_input_n_val->Hide(); sizer->Add(0, 0, 0, wxTOP, FromDIP(10)); - sizer->Add(ratio_text, 0, wxLEFT | wxRIGHT | wxEXPAND, FromDIP(20)); + sizer->Add(m_ratio_text, 0, wxLEFT | wxRIGHT | wxEXPAND, FromDIP(20)); sizer->Add(0, 0, 0, wxTOP, FromDIP(10)); sizer->Add(kn_val_sizer, 0, wxLEFT | wxRIGHT | wxEXPAND, FromDIP(20)); sizer->Add(0, 0, 0, wxTOP, FromDIP(10)); @@ -379,6 +388,62 @@ void AMSMaterialsSetting::enable_confirm_button(bool en) } } +void AMSMaterialsSetting::on_select_reset(wxCommandEvent& event) { + MessageDialog msg_dlg(nullptr, _L("Are you sure you want to clear the filament information?"), wxEmptyString, wxICON_WARNING | wxOK | wxCANCEL); + auto result = msg_dlg.ShowModal(); + if (result != wxID_OK) + return; + + m_input_nozzle_min->GetTextCtrl()->SetValue(""); + m_input_nozzle_max->GetTextCtrl()->SetValue(""); + ams_filament_id = ""; + ams_setting_id = ""; + wxString k_text = "0.000"; + wxString n_text = "0.000"; + m_filament_type = ""; + long nozzle_temp_min_int = 0; + long nozzle_temp_max_int = 0; + wxColour color = *wxWHITE; + char col_buf[10]; + sprintf(col_buf, "%02X%02X%02XFF", (int)color.Red(), (int)color.Green(), (int)color.Blue()); + + if (obj) { + // set filament + if (obj->is_support_filament_edit_virtual_tray || !is_virtual_tray()) { + if (is_virtual_tray()) { + obj->command_ams_filament_settings(255, VIRTUAL_TRAY_ID, ams_filament_id, ams_setting_id, std::string(col_buf), m_filament_type, nozzle_temp_min_int, nozzle_temp_max_int); + } + else { + obj->command_ams_filament_settings(ams_id, tray_id, ams_filament_id, ams_setting_id, std::string(col_buf), m_filament_type, nozzle_temp_min_int, nozzle_temp_max_int); + } + } + + // set k / n value + if (obj->is_function_supported(PrinterFunction::FUNC_VIRTUAL_TYAY)) { + // set extrusion cali ratio + int cali_tray_id = ams_id * 4 + tray_id; + + double k = 0.0; + try { + k_text.ToDouble(&k); + } + catch (...) { + ; + } + + double n = 0.0; + try { + n_text.ToDouble(&n); + } + catch (...) { + ; + } + obj->command_extrusion_cali_set(cali_tray_id, "", "", k, n); + } + } + Close(); +} + void AMSMaterialsSetting::on_select_ok(wxCommandEvent &event) { wxString k_text = m_input_k_val->GetTextCtrl()->GetValue(); @@ -408,10 +473,11 @@ void AMSMaterialsSetting::on_select_ok(wxCommandEvent &event) } obj->command_extrusion_cali_set(VIRTUAL_TRAY_ID, "", "", k, n); Close(); - } else { + } + else { if (!m_is_third) { // check and set k n - if (obj->is_function_supported(PrinterFunction::FUNC_EXTRUSION_CALI)) { + if (obj->is_function_supported(PrinterFunction::FUNC_VIRTUAL_TYAY)) { if (!ExtrusionCalibration::check_k_validation(k_text)) { wxString k_tips = _L("Please input a valid value (K in 0~0.5)"); wxString kn_tips = _L("Please input a valid value (K in 0~0.5, N in 0.6~2.0)"); @@ -423,7 +489,7 @@ void AMSMaterialsSetting::on_select_ok(wxCommandEvent &event) // set k / n value - if (obj->is_function_supported(PrinterFunction::FUNC_EXTRUSION_CALI)) { + if (obj->is_function_supported(PrinterFunction::FUNC_VIRTUAL_TYAY)) { // set extrusion cali ratio int cali_tray_id = ams_id * 4 + tray_id; @@ -448,34 +514,68 @@ void AMSMaterialsSetting::on_select_ok(wxCommandEvent &event) return; } wxString nozzle_temp_min = m_input_nozzle_min->GetTextCtrl()->GetValue(); - auto filament = m_comboBox_filament->GetValue(); + auto filament = m_comboBox_filament->GetValue(); wxString nozzle_temp_max = m_input_nozzle_max->GetTextCtrl()->GetValue(); long nozzle_temp_min_int, nozzle_temp_max_int; nozzle_temp_min.ToLong(&nozzle_temp_min_int); nozzle_temp_max.ToLong(&nozzle_temp_max_int); - wxColour color = m_clrData->GetColour(); + wxColour color = m_clr_picker->m_colour; char col_buf[10]; - sprintf(col_buf, "%02X%02X%02XFF", (int) color.Red(), (int) color.Green(), (int) color.Blue()); + sprintf(col_buf, "%02X%02X%02XFF", (int)color.Red(), (int)color.Green(), (int)color.Blue()); ams_filament_id = ""; ams_setting_id = ""; - PresetBundle *preset_bundle = wxGetApp().preset_bundle; + PresetBundle* preset_bundle = wxGetApp().preset_bundle; if (preset_bundle) { for (auto it = preset_bundle->filaments.begin(); it != preset_bundle->filaments.end(); it++) { + if (it->alias.compare(m_comboBox_filament->GetValue().ToStdString()) == 0) { + + + //check is it in the filament blacklist + if(!is_virtual_tray()){ + bool in_blacklist = false; + std::string action; + std::string info; + std::string filamnt_type; + it->get_filament_type(filamnt_type); + + if (it->vendor) { + DeviceManager::check_filaments_in_blacklist(it->vendor->name, filamnt_type, in_blacklist, action, info); + } + + if (in_blacklist) { + if (action == "prohibition") { + MessageDialog msg_wingow(nullptr, info, _L("Error"), wxICON_WARNING | wxOK); + msg_wingow.ShowModal(); + //m_comboBox_filament->SetSelection(m_filament_selection); + return; + } + else if (action == "warning") { + MessageDialog msg_wingow(nullptr, info, _L("Warning"), wxICON_INFORMATION | wxOK); + msg_wingow.ShowModal(); + } + } + } + ams_filament_id = it->filament_id; ams_setting_id = it->setting_id; + break; } } } if (ams_filament_id.empty() || nozzle_temp_min.empty() || nozzle_temp_max.empty() || m_filament_type.empty()) { BOOST_LOG_TRIVIAL(trace) << "Invalid Setting id"; - } else { + MessageDialog msg_dlg(nullptr, _L("You need to select the material type and color first."), wxEmptyString, wxICON_WARNING | wxOK); + msg_dlg.ShowModal(); + return; + } + else { if (obj) { - if (obj->is_function_supported(PrinterFunction::FUNC_EXTRUSION_CALI)) { + if (obj->is_function_supported(PrinterFunction::FUNC_VIRTUAL_TYAY)) { if (!ExtrusionCalibration::check_k_validation(k_text)) { wxString k_tips = _L("Please input a valid value (K in 0~0.5)"); wxString kn_tips = _L("Please input a valid value (K in 0~0.5, N in 0.6~2.0)"); @@ -489,13 +589,14 @@ void AMSMaterialsSetting::on_select_ok(wxCommandEvent &event) if (obj->is_support_filament_edit_virtual_tray || !is_virtual_tray()) { if (is_virtual_tray()) { obj->command_ams_filament_settings(255, VIRTUAL_TRAY_ID, ams_filament_id, ams_setting_id, std::string(col_buf), m_filament_type, nozzle_temp_min_int, nozzle_temp_max_int); - } else { + } + else { obj->command_ams_filament_settings(ams_id, tray_id, ams_filament_id, ams_setting_id, std::string(col_buf), m_filament_type, nozzle_temp_min_int, nozzle_temp_max_int); } } // set k / n value - if (obj->is_function_supported(PrinterFunction::FUNC_EXTRUSION_CALI)) { + if (obj->is_function_supported(PrinterFunction::FUNC_VIRTUAL_TYAY)) { // set extrusion cali ratio int cali_tray_id = ams_id * 4 + tray_id; @@ -529,15 +630,47 @@ void AMSMaterialsSetting::on_select_close(wxCommandEvent &event) void AMSMaterialsSetting::set_color(wxColour color) { - m_clrData->SetColour(color); + //m_clrData->SetColour(color); + m_clr_picker->set_color(color); } -void AMSMaterialsSetting::on_clr_picker(wxCommandEvent & event) +void AMSMaterialsSetting::set_colors(std::vector colors) +{ + //m_clrData->SetColour(color); + m_clr_picker->set_colors(colors); +} + + +void AMSMaterialsSetting::on_picker_color(wxCommandEvent& event) +{ + unsigned int color_num = event.GetInt(); + set_color(wxColour(color_num>>16&0xFF, color_num>>8&0xFF, color_num&0xFF)); +} + +void AMSMaterialsSetting::on_clr_picker(wxMouseEvent &event) { if(!m_is_third || obj->is_in_printing() || obj->can_resume()) return; - auto clr_dialog = new wxColourDialog(this, m_clrData); - show_flag = true; + + + std::vector ams_colors; + for (auto ams_it = obj->amsList.begin(); ams_it != obj->amsList.end(); ++ams_it) { + for (auto tray_id = ams_it->second->trayList.begin(); tray_id != ams_it->second->trayList.end(); ++tray_id) { + std::vector::iterator iter = find(ams_colors.begin(), ams_colors.end(), AmsTray::decode_color(tray_id->second->color)); + if (iter == ams_colors.end()) { + ams_colors.push_back(AmsTray::decode_color(tray_id->second->color)); + } + } + } + + wxPoint img_pos = m_clr_picker->ClientToScreen(wxPoint(0, 0)); + wxPoint popup_pos(img_pos.x + FromDIP(50), img_pos.y); + m_color_picker_popup.Position(popup_pos, wxSize(0, 0)); + m_color_picker_popup.set_ams_colours(ams_colors); + m_color_picker_popup.set_def_colour(m_clr_picker->m_colour); + m_color_picker_popup.Popup(); + + /*auto clr_dialog = new wxColourDialog(this, m_clrData); if (clr_dialog->ShowModal() == wxID_OK) { m_clrData = &(clr_dialog->GetColourData()); m_clr_picker->SetBackgroundColor(wxColour( @@ -546,7 +679,7 @@ void AMSMaterialsSetting::on_clr_picker(wxCommandEvent & event) m_clrData->GetColour().Blue(), 254 )); - } + }*/ } bool AMSMaterialsSetting::is_virtual_tray() @@ -565,7 +698,7 @@ void AMSMaterialsSetting::update_widgets() else m_panel_normal->Hide(); m_panel_kn->Show(); - } else if (obj && obj->is_function_supported(PrinterFunction::FUNC_EXTRUSION_CALI)) { + } else if (obj && obj->is_function_supported(PrinterFunction::FUNC_VIRTUAL_TYAY)) { m_panel_normal->Show(); m_panel_kn->Show(); } else { @@ -581,7 +714,21 @@ bool AMSMaterialsSetting::Show(bool show) m_button_confirm->SetMinSize(AMS_MATERIALS_SETTING_BUTTON_SIZE); m_input_nozzle_max->GetTextCtrl()->SetSize(wxSize(-1, FromDIP(20))); m_input_nozzle_min->GetTextCtrl()->SetSize(wxSize(-1, FromDIP(20))); - m_clr_picker->SetBackgroundColour(m_clr_picker->GetParent()->GetBackgroundColour()); + //m_clr_picker->set_color(m_clr_picker->GetParent()->GetBackgroundColour()); + + if (obj && obj->is_function_supported(PrinterFunction::FUNC_EXTRUSION_CALI)) { + m_ratio_text->Show(); + m_k_param->Show(); + m_input_k_val->Show(); + } + else { + m_ratio_text->Hide(); + m_k_param->Hide(); + m_input_k_val->Hide(); + } + Layout(); + Fit(); + wxGetApp().UpdateDarkUI(this); } return DPIDialog::Show(show); } @@ -599,6 +746,7 @@ void AMSMaterialsSetting::Popup(wxString filament, wxString sn, wxString temp_mi m_input_n_val->GetTextCtrl()->SetValue(n); if (is_virtual_tray() && obj && !obj->is_support_filament_edit_virtual_tray) { + m_button_reset->Show(); m_button_confirm->Show(); update(); Layout(); @@ -606,15 +754,16 @@ void AMSMaterialsSetting::Popup(wxString filament, wxString sn, wxString temp_mi ShowModal(); return; } else { - m_clr_picker->SetBackgroundColor(wxColour( + /* m_clr_picker->set_color(wxColour( m_clrData->GetColour().Red(), m_clrData->GetColour().Green(), m_clrData->GetColour().Blue(), 254 - )); + ));*/ if (!m_is_third) { - if (obj && obj->is_function_supported(PrinterFunction::FUNC_EXTRUSION_CALI)) { + m_button_reset->Hide(); + if (obj && obj->is_function_supported(PrinterFunction::FUNC_VIRTUAL_TYAY)) { m_button_confirm->Show(); } else { m_button_confirm->Hide(); @@ -636,6 +785,7 @@ void AMSMaterialsSetting::Popup(wxString filament, wxString sn, wxString temp_mi return; } + m_button_reset->Show(); m_button_confirm->Show(); m_panel_SN->Hide(); m_comboBox_filament->Show(); @@ -721,37 +871,18 @@ void AMSMaterialsSetting::post_select_event() { wxPostEvent(m_comboBox_filament, event); } +void AMSMaterialsSetting::msw_rescale() +{ + m_clr_picker->msw_rescale(); +} + void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt) { m_filament_type = ""; PresetBundle* preset_bundle = wxGetApp().preset_bundle; if (preset_bundle) { for (auto it = preset_bundle->filaments.begin(); it != preset_bundle->filaments.end(); it++) { - if (it->alias.compare(m_comboBox_filament->GetValue().ToStdString()) == 0) { - - //check is it in the filament blacklist - bool in_blacklist = false; - std::string action; - std::string info; - std::string filamnt_type; - it->get_filament_type(filamnt_type); - - if (it->vendor) { - DeviceManager::check_filaments_in_blacklist(it->vendor->name, filamnt_type, in_blacklist, action, info); - } - - if (in_blacklist) { - if (action == "prohibition") { - MessageDialog msg_wingow(nullptr, info, _L("Error"), wxICON_WARNING | wxOK); - msg_wingow.ShowModal(); - m_comboBox_filament->SetSelection(m_filament_selection); - return; - } - else if (action == "warning") { - MessageDialog msg_wingow(nullptr, info, _L("Warning"), wxICON_INFORMATION | wxOK); - msg_wingow.ShowModal(); - } - } + if (!m_comboBox_filament->GetValue().IsEmpty() && it->alias.compare(m_comboBox_filament->GetValue().ToStdString()) == 0) { // ) if nozzle_temperature_range is found ConfigOption* opt_min = it->config.option("nozzle_temperature_range_low"); @@ -789,10 +920,10 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt) } } if (m_input_nozzle_min->GetTextCtrl()->GetValue().IsEmpty()) { - m_input_nozzle_min->GetTextCtrl()->SetValue("220"); + m_input_nozzle_min->GetTextCtrl()->SetValue("0"); } if (m_input_nozzle_max->GetTextCtrl()->GetValue().IsEmpty()) { - m_input_nozzle_max->GetTextCtrl()->SetValue("220"); + m_input_nozzle_max->GetTextCtrl()->SetValue("0"); } m_filament_selection = evt.GetSelection(); @@ -800,4 +931,297 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt) void AMSMaterialsSetting::on_dpi_changed(const wxRect &suggested_rect) { this->Refresh(); } +ColorPicker::ColorPicker(wxWindow* parent, wxWindowID id, const wxPoint& pos /*= wxDefaultPosition*/, const wxSize& size /*= wxDefaultSize*/) +{ + wxWindow::Create(parent, id, pos, size); + + SetSize(wxSize(FromDIP(25), FromDIP(25))); + SetMinSize(wxSize(FromDIP(25), FromDIP(25))); + SetMaxSize(wxSize(FromDIP(25), FromDIP(25))); + + Bind(wxEVT_PAINT, &ColorPicker::paintEvent, this); + m_bitmap_border = create_scaled_bitmap("color_picker_border", nullptr, 25); +} + +ColorPicker::~ColorPicker(){} + +void ColorPicker::msw_rescale() +{ + m_bitmap_border = create_scaled_bitmap("color_picker_border", nullptr, 25); + Refresh(); +} + +void ColorPicker::set_color(wxColour col) +{ + m_colour = col; + Refresh(); +} + +void ColorPicker::set_colors(std::vector cols) +{ + m_cols = cols; + Refresh(); +} + +void ColorPicker::paintEvent(wxPaintEvent& evt) +{ + wxPaintDC dc(this); + render(dc); +} + +void ColorPicker::render(wxDC& dc) +{ +#ifdef __WXMSW__ + wxSize size = GetSize(); + wxMemoryDC memdc; + wxBitmap bmp(size.x, size.y); + memdc.SelectObject(bmp); + memdc.Blit({ 0, 0 }, size, &dc, { 0, 0 }); + + { + wxGCDC dc2(memdc); + doRender(dc2); + } + + memdc.SelectObject(wxNullBitmap); + dc.DrawBitmap(bmp, 0, 0); +#else + doRender(dc); +#endif +} + +void ColorPicker::doRender(wxDC& dc) +{ + wxSize size = GetSize(); + + auto radius = m_show_full?size.x / 2:size.x / 2 - FromDIP(1); + if (m_selected) radius -= FromDIP(1); + + dc.SetPen(wxPen(m_colour)); + dc.SetBrush(wxBrush(m_colour)); + dc.DrawCircle(size.x / 2, size.x / 2, radius); + + if (m_selected) { + dc.SetPen(wxPen(m_colour)); + dc.SetBrush(*wxTRANSPARENT_BRUSH); + dc.DrawCircle(size.x / 2, size.x / 2, size.x / 2); + } + + if (m_show_full) { + dc.SetPen(wxPen(wxColour(0x6B6B6B))); + dc.SetBrush(*wxTRANSPARENT_BRUSH); + dc.DrawCircle(size.x / 2, size.x / 2, radius); + + if (m_cols.size() > 1) { + int left = FromDIP(0); + float total_width = size.x; + int gwidth = std::round(total_width / (m_cols.size() - 1)); + + for (int i = 0; i < m_cols.size() - 1; i++) { + + if ((left + gwidth) > (size.x)) { + gwidth = size.x - left; + } + + auto rect = wxRect(left, 0, gwidth, size.y); + dc.GradientFillLinear(rect, m_cols[i], m_cols[i + 1], wxEAST); + left += gwidth; + } + dc.DrawBitmap(m_bitmap_border, wxPoint(0, 0)); + } + } +} + +ColorPickerPopup::ColorPickerPopup(wxWindow* parent) + :PopupWindow(parent, wxBORDER_NONE) +{ + m_def_colors.clear(); + m_def_colors.push_back(wxColour(0xFFFFFF)); + m_def_colors.push_back(wxColour(0xfff144)); + m_def_colors.push_back(wxColour(0xDCF478)); + m_def_colors.push_back(wxColour(0x0ACC38)); + m_def_colors.push_back(wxColour(0x057748)); + m_def_colors.push_back(wxColour(0x0d6284)); + m_def_colors.push_back(wxColour(0x0EE2A0)); + m_def_colors.push_back(wxColour(0x76D9F4)); + m_def_colors.push_back(wxColour(0x46a8f9)); + m_def_colors.push_back(wxColour(0x2850E0)); + m_def_colors.push_back(wxColour(0x443089)); + m_def_colors.push_back(wxColour(0xA03CF7)); + m_def_colors.push_back(wxColour(0xF330F9)); + m_def_colors.push_back(wxColour(0xD4B1DD)); + m_def_colors.push_back(wxColour(0xf95d73)); + m_def_colors.push_back(wxColour(0xf72323)); + m_def_colors.push_back(wxColour(0x7c4b00)); + m_def_colors.push_back(wxColour(0xf98c36)); + m_def_colors.push_back(wxColour(0xfcecd6)); + m_def_colors.push_back(wxColour(0xD3C5A3)); + m_def_colors.push_back(wxColour(0xAF7933)); + m_def_colors.push_back(wxColour(0x898989)); + m_def_colors.push_back(wxColour(0xBCBCBC)); + m_def_colors.push_back(wxColour(0x161616)); + + + SetBackgroundColour(wxColour(*wxWHITE)); + + wxBoxSizer* m_sizer_main = new wxBoxSizer(wxVERTICAL); + wxBoxSizer* m_sizer_box = new wxBoxSizer(wxVERTICAL); + + m_def_color_box = new StaticBox(this); + wxBoxSizer* m_sizer_ams = new wxBoxSizer(wxHORIZONTAL); + auto m_title_ams = new wxStaticText(m_def_color_box, wxID_ANY, _L("AMS"), wxDefaultPosition, wxDefaultSize, 0); + m_title_ams->SetFont(::Label::Body_14); + m_title_ams->SetBackgroundColour(wxColour(238, 238, 238)); + m_sizer_ams->Add(m_title_ams, 0, wxALL, 5); + auto ams_line = new wxPanel(m_def_color_box, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL); + ams_line->SetBackgroundColour(wxColour(0xCECECE)); + ams_line->SetMinSize(wxSize(-1, 1)); + ams_line->SetMaxSize(wxSize(-1, 1)); + m_sizer_ams->Add(ams_line, 1, wxALIGN_CENTER, 0); + + + m_def_color_box->SetCornerRadius(FromDIP(10)); + m_def_color_box->SetBackgroundColor(StateColor(std::pair(wxColour(238, 238, 238), StateColor::Normal))); + m_def_color_box->SetBorderColor(StateColor(std::pair(wxColour(238, 238, 238), StateColor::Normal))); + + //ams + m_ams_fg_sizer = new wxFlexGridSizer(0, 8, 0, 0); + m_ams_fg_sizer->SetFlexibleDirection(wxBOTH); + m_ams_fg_sizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); + + //other + wxFlexGridSizer* fg_sizer; + fg_sizer = new wxFlexGridSizer(0, 8, 0, 0); + fg_sizer->SetFlexibleDirection(wxBOTH); + fg_sizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); + + + for (wxColour col : m_def_colors) { + auto cp = new ColorPicker(m_def_color_box, wxID_ANY, wxDefaultPosition, wxDefaultSize); + cp->set_color(col); + cp->set_selected(false); + cp->SetBackgroundColour(StateColor::darkModeColorFor(wxColour(238,238,238))); + m_color_pickers.push_back(cp); + fg_sizer->Add(cp, 0, wxALL, FromDIP(3)); + cp->Bind(wxEVT_LEFT_DOWN, [this, cp](auto& e) { + set_def_colour(cp->m_colour); + + wxCommandEvent evt(EVT_SELECTED_COLOR); + unsigned long g_col = ((cp->m_colour.Red() & 0xff) << 16) + ((cp->m_colour.Green() & 0xff) << 8) + (cp->m_colour.Blue() & 0xff); + evt.SetInt(g_col); + wxPostEvent(GetParent(), evt); + }); + } + + wxBoxSizer* m_sizer_other = new wxBoxSizer(wxHORIZONTAL); + auto m_title_other = new wxStaticText(m_def_color_box, wxID_ANY, _L("Other color"), wxDefaultPosition, wxDefaultSize, 0); + m_title_other->SetFont(::Label::Body_14); + m_title_other->SetBackgroundColour(wxColour(238, 238, 238)); + m_sizer_other->Add(m_title_other, 0, wxALL, 5); + auto other_line = new wxPanel(m_def_color_box, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL); + other_line->SetMinSize(wxSize(-1, 1)); + other_line->SetMaxSize(wxSize(-1, 1)); + other_line->SetBackgroundColour(wxColour(0xCECECE)); + m_sizer_other->Add(other_line, 1, wxALIGN_CENTER, 0); + + m_sizer_box->Add(0, 0, 0, wxTOP, FromDIP(10)); + m_sizer_box->Add(m_sizer_ams, 1, wxEXPAND|wxLEFT|wxRIGHT, FromDIP(10)); + m_sizer_box->Add(m_ams_fg_sizer, 0, wxEXPAND|wxLEFT|wxRIGHT, FromDIP(10)); + m_sizer_box->Add(m_sizer_other, 1, wxEXPAND|wxLEFT|wxRIGHT, FromDIP(10)); + m_sizer_box->Add(fg_sizer, 0, wxEXPAND|wxLEFT|wxRIGHT, FromDIP(10)); + m_sizer_box->Add(0, 0, 0, wxTOP, FromDIP(10)); + + + m_def_color_box->SetSizer(m_sizer_box); + m_def_color_box->Layout(); + m_def_color_box->Fit(); + + m_sizer_main->Add(m_def_color_box, 0, wxALL | wxEXPAND, 10); + SetSizer(m_sizer_main); + Layout(); + Fit(); + + Bind(wxEVT_PAINT, &ColorPickerPopup::paintEvent, this); + wxGetApp().UpdateDarkUIWin(this); +} + + +void ColorPickerPopup::set_ams_colours(std::vector ams) +{ + if (m_ams_color_pickers.size() > 0) { + for (ColorPicker* col_pick:m_ams_color_pickers) { + + std::vector::iterator iter = find(m_color_pickers.begin(), m_color_pickers.end(), col_pick); + if (iter != m_color_pickers.end()) { + col_pick->Destroy(); + m_color_pickers.erase(iter); + } + } + + m_ams_color_pickers.clear(); + } + + + m_ams_colors = ams; + for (wxColour col : m_ams_colors) { + auto cp = new ColorPicker(m_def_color_box, wxID_ANY, wxDefaultPosition, wxDefaultSize); + cp->set_color(col); + cp->set_selected(false); + cp->SetBackgroundColour(StateColor::darkModeColorFor(wxColour(238,238,238))); + m_color_pickers.push_back(cp); + m_ams_color_pickers.push_back(cp); + m_ams_fg_sizer->Add(cp, 0, wxALL, FromDIP(3)); + cp->Bind(wxEVT_LEFT_DOWN, [this, cp](auto& e) { + set_def_colour(cp->m_colour); + + wxCommandEvent evt(EVT_SELECTED_COLOR); + unsigned long g_col = ((cp->m_colour.Red() & 0xff) << 16) + ((cp->m_colour.Green() & 0xff) << 8) + (cp->m_colour.Blue() & 0xff); + evt.SetInt(g_col); + wxPostEvent(GetParent(), evt); + }); + } + m_ams_fg_sizer->Layout(); + Layout(); + Fit(); +} + +void ColorPickerPopup::set_def_colour(wxColour col) +{ + m_def_col = col; + + for (ColorPicker* cp : m_color_pickers) { + if (cp->m_selected) { + cp->set_selected(false); + } + } + + for (ColorPicker* cp : m_color_pickers) { + if (cp->m_colour == m_def_col) { + cp->set_selected(true); + break; + } + } + + Dismiss(); +} + +void ColorPickerPopup::paintEvent(wxPaintEvent& evt) +{ + wxPaintDC dc(this); + dc.SetPen(wxColour(0xAC, 0xAC, 0xAC)); + dc.SetBrush(*wxTRANSPARENT_BRUSH); + dc.DrawRoundedRectangle(0, 0, GetSize().x, GetSize().y, 0); +} + +void ColorPickerPopup::OnDismiss() {} + +void ColorPickerPopup::Popup() +{ + PopupWindow::Popup(); +} + +bool ColorPickerPopup::ProcessLeftDown(wxMouseEvent& event) { + return PopupWindow::ProcessLeftDown(event); +} + }} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/AMSMaterialsSetting.hpp b/src/slic3r/GUI/AMSMaterialsSetting.hpp index a98140ae40..6c0b38e408 100644 --- a/src/slic3r/GUI/AMSMaterialsSetting.hpp +++ b/src/slic3r/GUI/AMSMaterialsSetting.hpp @@ -28,6 +28,54 @@ namespace Slic3r { namespace GUI { +class ColorPicker : public wxWindow +{ +public: + wxBitmap m_bitmap_border; + wxColour m_colour; + std::vector m_cols; + bool m_selected{false}; + bool m_show_full{false}; + + ColorPicker(wxWindow* parent, wxWindowID id, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize); + ~ColorPicker(); + + void msw_rescale(); + void set_color(wxColour col); + void set_colors(std::vector cols); + void set_selected(bool sel) {m_selected = sel;Refresh();}; + void set_show_full(bool full) {m_show_full = full;Refresh();}; + + void paintEvent(wxPaintEvent& evt); + void render(wxDC& dc); + void doRender(wxDC& dc); +}; + +class ColorPickerPopup : public PopupWindow +{ +public: + StaticBox* m_def_color_box; + wxFlexGridSizer* m_ams_fg_sizer; + wxColour m_def_col; + std::vector m_def_colors; + std::vector m_ams_colors; + std::vector m_color_pickers; + std::vector m_ams_color_pickers; + +public: + ColorPickerPopup(wxWindow* parent); + ~ColorPickerPopup() {}; + void set_ams_colours(std::vector ams); + void set_def_colour(wxColour col); + void paintEvent(wxPaintEvent& evt); + void Popup(); + virtual void OnDismiss() wxOVERRIDE; + virtual bool ProcessLeftDown(wxMouseEvent& event) wxOVERRIDE; + +public: +}; + + class AMSMaterialsSetting : public DPIDialog { public: @@ -46,10 +94,12 @@ public: wxString k = wxEmptyString, wxString n = wxEmptyString); void post_select_event(); - + void msw_rescale(); void set_color(wxColour color); + void set_colors(std::vector colors); - MachineObject *obj{nullptr}; + void on_picker_color(wxCommandEvent& color); + MachineObject* obj{ nullptr }; int ams_id { 0 }; /* 0 ~ 3 */ int tray_id { 0 }; /* 0 ~ 3 */ @@ -62,6 +112,7 @@ public: wxString m_brand_tmp; wxColour m_brand_colour; std::string m_filament_type; + ColorPickerPopup m_color_picker_popup; protected: void create_panel_normal(wxWindow* parent); @@ -69,8 +120,9 @@ protected: void on_dpi_changed(const wxRect &suggested_rect) override; void on_select_filament(wxCommandEvent& evt); void on_select_ok(wxCommandEvent &event); + void on_select_reset(wxCommandEvent &event); void on_select_close(wxCommandEvent &event); - void on_clr_picker(wxCommandEvent &event); + void on_clr_picker(wxMouseEvent &event); bool is_virtual_tray(); void update_widgets(); @@ -87,13 +139,15 @@ protected: wxStaticText * m_title_temperature; TextInput * m_input_nozzle_min; TextInput* m_input_nozzle_max; + Button * m_button_reset; Button * m_button_confirm; wxStaticText* m_tip_readonly; Button * m_button_close; - Button * m_clr_picker; + ColorPicker * m_clr_picker; wxColourData * m_clrData; wxPanel * m_panel_kn; + wxStaticText* m_ratio_text; wxStaticText* m_k_param; TextInput* m_input_k_val; wxStaticText* m_n_param; @@ -108,6 +162,8 @@ protected: TextInput* m_readonly_filament; }; +wxDECLARE_EVENT(EVT_SELECTED_COLOR, wxCommandEvent); + }} // namespace Slic3r::GUI #endif diff --git a/src/slic3r/GUI/AMSSetting.cpp b/src/slic3r/GUI/AMSSetting.cpp index b1377aa665..0818f5445e 100644 --- a/src/slic3r/GUI/AMSSetting.cpp +++ b/src/slic3r/GUI/AMSSetting.cpp @@ -163,7 +163,7 @@ void AMSSetting::create() m_checkbox_switch_filament->Bind(wxEVT_TOGGLEBUTTON, &AMSSetting::on_switch_filament, this); m_sizer_switch_filament->Add(m_checkbox_switch_filament, 0, wxTOP, 1); m_sizer_switch_filament->Add(0, 0, 0, wxLEFT, 12); - m_title_switch_filament = new wxStaticText(m_panel_body, wxID_ANY, _L("AMS auto switch filament"), wxDefaultPosition, wxDefaultSize, 0); + m_title_switch_filament = new wxStaticText(m_panel_body, wxID_ANY, _L("AMS filament backup"), wxDefaultPosition, wxDefaultSize, 0); m_title_switch_filament->SetFont(::Label::Head_13); m_title_switch_filament->SetForegroundColour(AMS_SETTING_GREY800); m_title_switch_filament->Wrap(AMS_SETTING_BODY_WIDTH); diff --git a/src/slic3r/GUI/AboutDialog.cpp b/src/slic3r/GUI/AboutDialog.cpp index 64fdc9ae48..6ffe0790d7 100644 --- a/src/slic3r/GUI/AboutDialog.cpp +++ b/src/slic3r/GUI/AboutDialog.cpp @@ -272,7 +272,7 @@ AboutDialog::AboutDialog() wxBoxSizer *text_sizer_horiz = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer *text_sizer = new wxBoxSizer(wxVERTICAL); - text_sizer_horiz->Add( 0, 0, 0, wxLEFT, FromDIP(23)); + text_sizer_horiz->Add( 0, 0, 0, wxLEFT, FromDIP(20)); std::vector text_list; text_list.push_back(_L("OrcaSlicer is based on BambuStudio, PrusaSlicer, and SuperSlicer.")); @@ -318,8 +318,7 @@ AboutDialog::AboutDialog() wxBoxSizer *copyright_ver_sizer = new wxBoxSizer(wxVERTICAL); wxBoxSizer *copyright_hor_sizer = new wxBoxSizer(wxHORIZONTAL); - copyright_hor_sizer->Add(copyright_ver_sizer, 0, wxALL,5); - copyright_hor_sizer->Add( 0, 0, 0, wxLEFT, FromDIP(120)); + copyright_hor_sizer->Add(copyright_ver_sizer, 0, wxLEFT, FromDIP(20)); wxStaticText *html_text = new wxStaticText(this, wxID_ANY, "Copyright(C) 2022-2023 Li Jiang All Rights Reserved", wxDefaultPosition, wxDefaultSize); html_text->SetForegroundColour(wxColour(107, 107, 107)); @@ -364,10 +363,10 @@ AboutDialog::AboutDialog() copyright_button_ver->Add( 0, 0, 0, wxTOP, FromDIP(10)); copyright_button_ver->Add(button_portions, 0, wxALL,0); - copyright_hor_sizer->Add(copyright_button_ver, 0, wxALL,0); - copyright_hor_sizer->Add( 0, 0, 0, wxRIGHT, FromDIP(13)); + copyright_hor_sizer->AddStretchSpacer(); + copyright_hor_sizer->Add(copyright_button_ver, 0, wxRIGHT, FromDIP(20)); - ver_sizer->Add(copyright_hor_sizer, 0, wxALIGN_CENTER_HORIZONTAL|wxALL,0); + ver_sizer->Add(copyright_hor_sizer, 0, wxEXPAND ,0); ver_sizer->Add( 0, 0, 0, wxTOP, FromDIP(30)); button_portions->Bind(wxEVT_BUTTON, &AboutDialog::onCopyrightBtn, this); diff --git a/src/slic3r/GUI/AmsMappingPopup.cpp b/src/slic3r/GUI/AmsMappingPopup.cpp index d3ed3d5b06..d4f75e20d4 100644 --- a/src/slic3r/GUI/AmsMappingPopup.cpp +++ b/src/slic3r/GUI/AmsMappingPopup.cpp @@ -273,6 +273,14 @@ void MaterialItem::doRender(wxDC &dc) SetSizer(m_sizer_main); Layout(); Fit(); + + Bind(wxEVT_SHOW, [this](wxShowEvent& e) { + if (e.IsShown() && m_parent_item) { + wxPoint pos = m_parent_item->ClientToScreen(wxPoint(0, 0)); + pos.y += m_parent_item->GetRect().height; + this->Move(pos); + } + }); } wxString AmsMapingPopup::format_text(wxString &m_msg) @@ -772,47 +780,48 @@ AmsHumidityTipPopup::AmsHumidityTipPopup(wxWindow* parent) wxBoxSizer* m_sizer_tips = new wxBoxSizer(wxVERTICAL); m_staticText1 = new Label(this, _L("Cabin humidity")); - m_staticText1->SetForegroundColour(wxColour(0x352F2D)); m_staticText1->SetFont(::Label::Head_13); m_staticText2 = new Label(this, _L("Green means that AMS humidity is normal, orange represent humidity is high, red represent humidity is too high.(Hygrometer: lower the better.)")); m_staticText2->SetFont(::Label::Body_13); - m_staticText2->SetSize(wxSize(FromDIP(360), -1)); - m_staticText2->SetMinSize(wxSize(FromDIP(360), -1)); - m_staticText2->SetMaxSize(wxSize(FromDIP(360), -1)); - m_staticText2->Wrap(FromDIP(360)); + m_staticText2->SetSize(wxSize(FromDIP(357), -1)); + m_staticText2->SetMinSize(wxSize(FromDIP(357), -1)); + m_staticText2->SetMaxSize(wxSize(FromDIP(357), -1)); + m_staticText2->Wrap(FromDIP(357)); m_staticText3 = new Label(this, _L("Desiccant status")); - m_staticText3->SetForegroundColour(wxColour(0x352F2D)); m_staticText3->SetFont(::Label::Head_13); m_staticText4 = new Label(this, _L("A desiccant status lower than two bars indicates that desiccant may be inactive. Please change the desiccant.(The bars: higher the better.)")); m_staticText4->SetFont(::Label::Body_13); - m_staticText4->SetSize(wxSize(FromDIP(360), -1)); - m_staticText4->SetMinSize(wxSize(FromDIP(360), -1)); - m_staticText4->SetMaxSize(wxSize(FromDIP(360), -1)); - m_staticText4->Wrap(FromDIP(360)); + m_staticText4->SetSize(wxSize(FromDIP(357), -1)); + m_staticText4->SetMinSize(wxSize(FromDIP(357), -1)); + m_staticText4->SetMaxSize(wxSize(FromDIP(357), -1)); + m_staticText4->Wrap(FromDIP(357)); - m_sizer_tips->Add(m_staticText1, 0, wxALL, 3); - m_sizer_tips->Add(m_staticText2, 0, wxALL, 3); - m_sizer_tips->Add(m_staticText3, 0, wxALL, 3); - m_sizer_tips->Add(m_staticText4, 0, wxALL, 3); + m_sizer_tips->Add(m_staticText1, 0, wxLEFT|wxRIGHT, 3); + m_sizer_tips->Add(0,0,0,wxTOP,2); + m_sizer_tips->Add(m_staticText2, 0, wxLEFT|wxRIGHT, 3); + m_sizer_tips->Add(0,0,0,wxTOP,8); + m_sizer_tips->Add(m_staticText3, 0, wxLEFT|wxRIGHT, 3); + m_sizer_tips->Add(0,0,0,wxTOP,2); + m_sizer_tips->Add(m_staticText4, 0, wxLEFT|wxRIGHT, 3); m_sizer_body->Add(m_sizer_tips, 0, wxEXPAND, 0); - main_sizer->Add(m_sizer_body, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(30)); + main_sizer->Add(m_sizer_body, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(20)); m_staticText_note = new Label(this, _L("Note: When the lid is open or the desiccant pack is changed, it can take hours or a night to absorb the moisture. Low temperatures also slow down the process. During this time, the indicator may not represent the chamber accurately.")); m_staticText4->SetFont(::Label::Body_13); - m_staticText_note->SetMinSize(wxSize(FromDIP(536), -1)); - m_staticText_note->SetMaxSize(wxSize(FromDIP(536), -1)); - m_staticText_note->Wrap(FromDIP(536)); - main_sizer->Add(m_staticText_note, 0, wxALL | wxLEFT | wxRIGHT, 34); + m_staticText_note->SetMinSize(wxSize(FromDIP(523), -1)); + m_staticText_note->SetMaxSize(wxSize(FromDIP(523), -1)); + m_staticText_note->Wrap(FromDIP(523)); + main_sizer->Add(m_staticText_note, 0, wxALL | wxLEFT | wxRIGHT, 22); m_button_confirm = new Button(this, _L("OK")); StateColor btn_bg_green(std::pair(wxColour(27, 136, 68), StateColor::Pressed), std::pair(wxColour(0, 150, 136), StateColor::Normal)); @@ -1106,4 +1115,382 @@ void MappingContainer::doRender(wxDC& dc) dc.DrawBitmap(ams_mapping_item_container, 0, 0); } +AmsReplaceMaterialDialog::AmsReplaceMaterialDialog(wxWindow* parent) + : DPIDialog(parent, wxID_ANY, _L("Filaments replace"), wxDefaultPosition, wxDefaultSize, wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX) +{ + SetBackgroundColour(*wxWHITE); + create(); + wxGetApp().UpdateDlgDarkUI(this); +} + +void AmsReplaceMaterialDialog::create() +{ + SetSize(wxSize(FromDIP(376), -1)); + SetMinSize(wxSize(FromDIP(376), -1)); + SetMaxSize(wxSize(FromDIP(376), -1)); + + // set icon for dialog + std::string icon_path = (boost::format("%1%/images/BambuStudioTitle.ico") % resources_dir()).str(); + SetIcon(wxIcon(encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO)); + SetSizeHints(wxDefaultSize, wxDefaultSize); + + m_main_sizer = new wxBoxSizer(wxVERTICAL); + auto m_top_line = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL); + m_top_line->SetBackgroundColour(wxColour(166, 169, 170)); + m_main_sizer->Add(m_top_line, 0, wxEXPAND, 0); + + + auto m_button_sizer = new wxBoxSizer(wxHORIZONTAL); + + StateColor btn_bg_white(std::pair(AMS_CONTROL_DISABLE_COLOUR, StateColor::Disabled), + std::pair(AMS_CONTROL_DISABLE_COLOUR, StateColor::Pressed), + std::pair(AMS_CONTROL_DEF_BLOCK_BK_COLOUR, StateColor::Hovered), + std::pair(AMS_CONTROL_WHITE_COLOUR, StateColor::Normal)); + + StateColor btn_bd_white(std::pair(wxColour(255, 255, 254), StateColor::Disabled), + std::pair(wxColour(38, 46, 48), StateColor::Enabled)); + + StateColor btn_text_white(std::pair(wxColour(255, 255, 254), StateColor::Disabled), + std::pair(wxColour(38, 46, 48), StateColor::Enabled)); + + auto m_button_close = new Button(this, _L("Close")); + m_button_close->SetCornerRadius(FromDIP(11)); + m_button_close->SetBackgroundColor(btn_bg_white); + m_button_close->SetBorderColor(btn_bd_white); + m_button_close->SetTextColor(btn_text_white); + m_button_close->SetFont(Label::Body_13); + m_button_close->SetMinSize(wxSize(FromDIP(42), FromDIP(24))); + m_button_close->Bind(wxEVT_BUTTON, [this](auto& e) { + EndModal(wxCLOSE); + }); + + m_button_sizer->Add( 0, 0, 1, wxEXPAND, 0 ); + m_button_sizer->Add(m_button_close, 0, wxALIGN_CENTER, 0); + + + m_groups_sizer = new wxBoxSizer(wxVERTICAL); + m_main_sizer->Add(0,0,0, wxTOP, FromDIP(12)); + m_main_sizer->Add(m_groups_sizer,0,wxEXPAND|wxLEFT|wxRIGHT, FromDIP(16)); + m_main_sizer->Add(0,0,0, wxTOP, FromDIP(20)); + m_main_sizer->Add(m_button_sizer,0,wxEXPAND|wxLEFT|wxRIGHT, FromDIP(16)); + m_main_sizer->Add(0,0,0, wxTOP, FromDIP(20)); + + + CenterOnParent(); + SetSizer(m_main_sizer); + Layout(); + Fit(); +} + +std::vector AmsReplaceMaterialDialog::GetStatus(unsigned int status) +{ + std::vector listStatus; + bool current = false; + for (int i = 0; i < 16; i++) { + if (status & (1 << i)) { + current = true; + } + else { + current = false; + } + listStatus.push_back(current); + } + return listStatus; +} + +void AmsReplaceMaterialDialog::update_machine_obj(MachineObject* obj) +{ + if (obj) {m_obj = obj;} + else {return;} + + AmsTray* tray_list[4*4]; + for (auto i = 0; i < 4*4; i++) { + tray_list[i] = nullptr; + } + + try { + for (auto ams_info : obj->amsList) { + int ams_id_int = atoi(ams_info.first.c_str()) * 4; + + for (auto tray_info : ams_info.second->trayList) { + int tray_id_int = atoi(tray_info.first.c_str()); + tray_id_int = ams_id_int + tray_id_int; + tray_list[tray_id_int] = tray_info.second; + } + } + } + catch (...) {} + + //creat group + int group_index = 1; + for (int filam : m_obj->filam_bak) { + auto status_list = GetStatus(filam); + + wxColour group_color; + std::string group_material; + + //get color & material + for (auto i = 0; i < status_list.size(); i++) { + if (status_list[i] && tray_list[i] != nullptr) { + group_color = AmsTray::decode_color(tray_list[i]->color); + group_material = tray_list[i]->get_display_filament_type(); + } + } + + m_groups_sizer->Add(create_split_line(wxString::Format("%s%d", _L("Group"), group_index), group_color, group_material, status_list), 0, wxEXPAND, 0); + m_groups_sizer->Add(0, 0, 0, wxTOP, FromDIP(12)); + group_index++; + } + + Layout(); + Fit(); +} + +wxWindow* AmsReplaceMaterialDialog::create_split_line(wxString gname, wxColour col, wxString material, std::vector status_list) +{ + wxColour background_color = wxColour(0xF4F4F4); + + if (abs(col.Red() - background_color.Red()) <= 5 && + abs(col.Green() - background_color.Green()) <= 5 && + abs(col.Blue() - background_color.Blue()) <= 5) { + background_color = wxColour(0xE6E6E6); + } + + auto m_panel_group = new StaticBox(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); + m_panel_group->SetCornerRadius(FromDIP(4)); + m_panel_group->SetBackgroundColor(StateColor(std::pair(background_color, StateColor::Normal))); + + m_panel_group->SetSize(wxSize(FromDIP(344), -1)); + m_panel_group->SetMinSize(wxSize(FromDIP(344), -1)); + m_panel_group->SetMaxSize(wxSize(FromDIP(344), -1)); + + wxBoxSizer* group_sizer = new wxBoxSizer(wxVERTICAL); + + //group title + wxBoxSizer* title_sizer = new wxBoxSizer(wxHORIZONTAL); + auto group_name = new Label(m_panel_group, gname); + group_name->SetFont(::Label::Head_12); + + Button* material_info = new Button(m_panel_group, material); + material_info->SetFont(Label::Head_12); + material_info->SetCornerRadius(FromDIP(2)); + material_info->SetBorderColor(background_color); + + if (col.GetLuminance() < 0.5) + material_info->SetTextColor(*wxWHITE); + else + material_info->SetTextColor(0x6B6B6B); + + material_info->SetMinSize(wxSize(-1, FromDIP(24))); + material_info->SetBackgroundColor(col); + + + title_sizer->Add(group_name, 0, wxALIGN_CENTER, 0); + title_sizer->Add(0, 0, 0, wxLEFT, FromDIP(10)); + title_sizer->Add(material_info, 0, wxALIGN_CENTER, 0); + + + //group item + wxGridSizer* grid_Sizer = new wxGridSizer(0, 8, 0, 0); + + for (int i = 0; i < status_list.size(); i++) { + if (status_list[i]) { + AmsRMItem* amsitem = new AmsRMItem(m_panel_group, wxID_ANY, wxDefaultPosition, wxDefaultSize); + amsitem->set_color(col); + + //set current tray + if (!m_obj->m_tray_now.empty() && m_obj->m_tray_now == std::to_string(i)) { + amsitem->set_focus(true); + } + + amsitem->set_type(RMTYPE_NORMAL); + amsitem->set_index(wxGetApp().transition_tridid(i).ToStdString()); + amsitem->SetBackgroundColour(background_color); + grid_Sizer->Add(amsitem, 0, wxALIGN_CENTER | wxTOP | wxBottom, FromDIP(10)); + } + } + + //add the first tray + for (int i = 0; i < status_list.size(); i++) { + if (status_list[i]) { + AmsRMItem* amsitem = new AmsRMItem(m_panel_group, wxID_ANY, wxDefaultPosition, wxDefaultSize); + amsitem->set_color(col); + amsitem->set_type(RMTYPE_VIRTUAL); + amsitem->set_index(wxGetApp().transition_tridid(i).ToStdString()); + amsitem->SetBackgroundColour(background_color); + grid_Sizer->Add(amsitem, 0, wxALIGN_CENTER | wxTOP | wxBottom, FromDIP(10)); + break; + } + } + + group_sizer->Add(0, 0, 0, wxTOP, FromDIP(10)); + group_sizer->Add(title_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(12)); + group_sizer->Add(grid_Sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(12)); + group_sizer->Add(0, 0, 0, wxTOP, FromDIP(10)); + + m_panel_group->SetSizer(group_sizer); + m_panel_group->Layout(); + group_sizer->Fit(m_panel_group); + return m_panel_group; +} + +void AmsReplaceMaterialDialog::paintEvent(wxPaintEvent& evt) +{ + wxPaintDC dc(this); + dc.SetPen(wxColour(0xAC, 0xAC, 0xAC)); + dc.SetBrush(*wxTRANSPARENT_BRUSH); + dc.DrawRoundedRectangle(0, 0, GetSize().x, GetSize().y, 0); +} + +void AmsReplaceMaterialDialog::on_dpi_changed(const wxRect& suggested_rect) +{ + +} + +AmsRMItem::AmsRMItem(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size) +{ + wxWindow::Create(parent, id, pos, size); + + SetSize(wxSize(FromDIP(42), FromDIP(32))); + SetMinSize(wxSize(FromDIP(42), FromDIP(32))); + SetMaxSize(wxSize(FromDIP(42), FromDIP(32))); + + SetBackgroundColour(*wxWHITE); + + Bind(wxEVT_PAINT, &AmsRMItem::paintEvent, this); +} + +void AmsRMItem::paintEvent(wxPaintEvent& evt) +{ + wxPaintDC dc(this); + render(dc); +} + +void AmsRMItem::render(wxDC& dc) +{ +#ifdef __WXMSW__ + wxSize size = GetSize(); + wxMemoryDC memdc; + wxBitmap bmp(size.x, size.y); + memdc.SelectObject(bmp); + memdc.Blit({ 0, 0 }, size, &dc, { 0, 0 }); + + { + wxGCDC dc2(memdc); + doRender(dc2); + } + + memdc.SelectObject(wxNullBitmap); + dc.DrawBitmap(bmp, 0, 0); +#else + doRender(dc); +#endif +} + +void AmsRMItem::doRender(wxDC& dc) +{ + wxSize size = GetSize(); + + if (m_type == RMTYPE_NORMAL) { + dc.SetPen(wxPen(m_color, 2)); + dc.SetBrush(*wxTRANSPARENT_BRUSH); + } + else { + dc.SetPen(wxPen(m_color, 2, wxSHORT_DASH)); + dc.SetBrush(*wxTRANSPARENT_BRUSH); + } + + //top bottom line + dc.DrawLine(FromDIP(0), FromDIP(4), size.x - FromDIP(5), FromDIP(4)); + dc.DrawLine(FromDIP(0), size.y - FromDIP(4), size.x - FromDIP(5), size.y - FromDIP(4)); + + //left right line + dc.DrawLine(FromDIP(1), FromDIP(4), FromDIP(1), FromDIP(11)); + dc.DrawLine(FromDIP(1), FromDIP(22), FromDIP(1), size.y - FromDIP(4)); + + dc.DrawLine(size.x - FromDIP(5), FromDIP(4), size.x - FromDIP(5), FromDIP(11)); + dc.DrawLine(size.x - FromDIP(5), FromDIP(22), size.x - FromDIP(5), size.y - FromDIP(4)); + + //delta + dc.DrawLine(FromDIP(0), FromDIP(11), FromDIP(5), size.y / 2); + dc.DrawLine(FromDIP(0), FromDIP(22), FromDIP(5), size.y / 2); + + dc.DrawLine(size.x - FromDIP(5), FromDIP(11), size.x - FromDIP(1), size.y / 2); + dc.DrawLine(size.x - FromDIP(5), FromDIP(22), size.x - FromDIP(1), size.y / 2); + + + if (m_focus) { + dc.SetPen(wxPen(wxColour(0x00AE42), 2)); + dc.SetBrush(*wxTRANSPARENT_BRUSH); + dc.DrawLine(FromDIP(0), FromDIP(1), size.x - FromDIP(5), FromDIP(1)); + dc.DrawLine(FromDIP(0), size.y - FromDIP(1), size.x - FromDIP(5), size.y - FromDIP(1)); + } + + if (m_selected) { + } + + auto tsize = dc.GetMultiLineTextExtent(m_index); + auto tpot = wxPoint((size.x - tsize.x) / 2 - FromDIP(2), (size.y - tsize.y) / 2 + FromDIP(2)); + dc.SetTextForeground(wxColour(0x6B6B6B)); + dc.SetFont(::Label::Head_12); + dc.DrawText(m_index, tpot); +} + +AmsRMArrow::AmsRMArrow(wxWindow* parent) +{ + + wxWindow::Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize); + SetBackgroundColour(*wxWHITE); + Bind(wxEVT_PAINT, &AmsRMArrow::paintEvent, this); + + m_bitmap_left = ScalableBitmap(this, "replace_arrow_left", 7); + m_bitmap_right = ScalableBitmap(this, "replace_arrow_right", 7); + m_bitmap_down = ScalableBitmap(this, "replace_arrow_down", 7); + + + SetSize(wxSize(FromDIP(16), FromDIP(32))); + SetMinSize(wxSize(FromDIP(16), FromDIP(32))); + SetMaxSize(wxSize(FromDIP(16), FromDIP(32))); +} + +void AmsRMArrow::paintEvent(wxPaintEvent& evt) +{ + wxPaintDC dc(this); + render(dc); +} + +void AmsRMArrow::render(wxDC& dc) +{ +#ifdef __WXMSW__ + wxSize size = GetSize(); + wxMemoryDC memdc; + wxBitmap bmp(size.x, size.y); + memdc.SelectObject(bmp); + memdc.Blit({ 0, 0 }, size, &dc, { 0, 0 }); + + { + wxGCDC dc2(memdc); + doRender(dc2); + } + + memdc.SelectObject(wxNullBitmap); + dc.DrawBitmap(bmp, 0, 0); +#else + doRender(dc); +#endif +} + +void AmsRMArrow::doRender(wxDC& dc) +{ + wxSize size = GetSize(); + + dc.SetPen(wxPen(wxColour(0, 174, 66))); + dc.SetBrush(*wxTRANSPARENT_BRUSH); + + + dc.SetPen(wxPen(wxColour(0xACACAC))); + dc.SetBrush(wxBrush(wxColour(0xACACAC))); + dc.DrawCircle(size.x / 2, size.y / 2, FromDIP(7)); +} + }} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/AmsMappingPopup.hpp b/src/slic3r/GUI/AmsMappingPopup.hpp index 06fadaa82a..d1094a3859 100644 --- a/src/slic3r/GUI/AmsMappingPopup.hpp +++ b/src/slic3r/GUI/AmsMappingPopup.hpp @@ -66,6 +66,7 @@ public: MaterialItem(wxWindow *parent,wxColour mcolour, wxString mname); ~MaterialItem(); + wxPanel* m_main_panel; wxColour m_material_coloul; wxString m_material_name; @@ -129,7 +130,6 @@ class AmsMapingPopup : public PopupWindow { public: AmsMapingPopup(wxWindow *parent); - wxString format_text(wxString &m_msg); ~AmsMapingPopup(){}; wxStaticText * m_warning_text{nullptr}; @@ -143,7 +143,9 @@ public: std::string m_tag_material; wxBoxSizer *m_sizer_main{nullptr}; wxBoxSizer *m_sizer_list{nullptr}; + wxWindow *m_parent_item{nullptr}; + wxString format_text(wxString &m_msg); void update_materials_list(std::vector list); void set_tag_texture(std::string texture); void update_ams_data(std::map amsList); @@ -155,6 +157,7 @@ public: virtual void OnDismiss() wxOVERRIDE; virtual bool ProcessLeftDown(wxMouseEvent &event) wxOVERRIDE; void paintEvent(wxPaintEvent &evt); + void set_parent_item(wxWindow* item) {m_parent_item = item;}; std::vector parse_ams_mapping(std::map amsList); }; @@ -239,6 +242,75 @@ public: }; +class AmsReplaceMaterialDialog : public DPIDialog +{ +public: + AmsReplaceMaterialDialog(wxWindow* parent); + ~AmsReplaceMaterialDialog() {}; + +public: + wxWindow* create_split_line(wxString gname, wxColour col, wxString material, std::vector status_list); + void create(); + void update_machine_obj(MachineObject* obj); + void on_left_down(wxMouseEvent& evt); + void paintEvent(wxPaintEvent& evt); + void on_dpi_changed(const wxRect &suggested_rect) override; + std::vector GetStatus(unsigned int status); + +public: + wxBoxSizer* m_main_sizer{nullptr}; + wxBoxSizer* m_groups_sizer{nullptr}; + + MachineObject* m_obj{nullptr}; +}; + + +enum RMTYPE { + RMTYPE_NORMAL = 0, + RMTYPE_VIRTUAL = 1, +}; + +class AmsRMItem : public wxWindow +{ +public: + AmsRMItem(wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize); + ~AmsRMItem() {}; + +public: + void set_color(wxColour col) {m_color = col;}; + void set_type(RMTYPE type) {m_type = type;}; + void set_index(std::string index) {m_index = index;}; + void set_focus(bool focus) {m_focus = focus;}; + + void paintEvent(wxPaintEvent& evt); + void render(wxDC& dc); + void doRender(wxDC& dc); + +private: + RMTYPE m_type; + wxColour m_color; + std::string m_index; + bool m_focus = false; + bool m_selected = false; +}; + +class AmsRMArrow : public wxWindow +{ +public: + AmsRMArrow(wxWindow* parent); + ~AmsRMArrow() {}; + +public: + void paintEvent(wxPaintEvent& evt); + void render(wxDC& dc); + void doRender(wxDC& dc); + +private: + ScalableBitmap m_bitmap_left; + ScalableBitmap m_bitmap_right; + ScalableBitmap m_bitmap_down; +}; + wxDECLARE_EVENT(EVT_SET_FINISH_MAPPING, wxCommandEvent); }} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Auxiliary.cpp b/src/slic3r/GUI/Auxiliary.cpp index f94ee1170c..614d4f7f47 100644 --- a/src/slic3r/GUI/Auxiliary.cpp +++ b/src/slic3r/GUI/Auxiliary.cpp @@ -1034,7 +1034,6 @@ void AuxiliaryPanel::Reload(wxString aux_path) auto iter = m_paths_list.find(folder.ToStdString()); auto file_path_str = fs::path(file_path.c_str()); - if (iter != m_paths_list.end()) { m_paths_list[folder.ToStdString()].push_back(file_path_str); break; diff --git a/src/slic3r/GUI/BBLStatusBar.cpp b/src/slic3r/GUI/BBLStatusBar.cpp index 448b074aa0..fcf5205e85 100644 --- a/src/slic3r/GUI/BBLStatusBar.cpp +++ b/src/slic3r/GUI/BBLStatusBar.cpp @@ -128,6 +128,11 @@ void BBLStatusBar::clear_percent() } +void BBLStatusBar::show_networking_test(wxString msg) +{ + +} + void BBLStatusBar::show_progress(bool show) { if (show) { diff --git a/src/slic3r/GUI/BBLStatusBar.hpp b/src/slic3r/GUI/BBLStatusBar.hpp index ec3864193b..20cd96699f 100644 --- a/src/slic3r/GUI/BBLStatusBar.hpp +++ b/src/slic3r/GUI/BBLStatusBar.hpp @@ -45,6 +45,7 @@ public: int get_range() const override; void set_range(int = 100) override; void clear_percent() override; + void show_networking_test(wxString msg) override; void show_progress(bool); void start_busy(int = 100); void stop_busy(); diff --git a/src/slic3r/GUI/BBLStatusBarBind.cpp b/src/slic3r/GUI/BBLStatusBarBind.cpp index 2117e60794..76eafce693 100644 --- a/src/slic3r/GUI/BBLStatusBarBind.cpp +++ b/src/slic3r/GUI/BBLStatusBarBind.cpp @@ -97,6 +97,11 @@ void BBLStatusBarBind::clear_percent() } +void BBLStatusBarBind::show_networking_test(wxString msg) +{ + +} + void BBLStatusBarBind::show_progress(bool show) { if (show) { diff --git a/src/slic3r/GUI/BBLStatusBarBind.hpp b/src/slic3r/GUI/BBLStatusBarBind.hpp index e30fff085c..2752a7bd45 100644 --- a/src/slic3r/GUI/BBLStatusBarBind.hpp +++ b/src/slic3r/GUI/BBLStatusBarBind.hpp @@ -48,6 +48,7 @@ public: int get_range() const override; void set_range(int = 100) override; void clear_percent() override; + void show_networking_test(wxString msg) override; void show_progress(bool); void start_busy(int = 100); void stop_busy(); diff --git a/src/slic3r/GUI/BBLStatusBarSend.cpp b/src/slic3r/GUI/BBLStatusBarSend.cpp index e3dd7f9869..b162412ac0 100644 --- a/src/slic3r/GUI/BBLStatusBarSend.cpp +++ b/src/slic3r/GUI/BBLStatusBarSend.cpp @@ -29,23 +29,18 @@ BBLStatusBarSend::BBLStatusBarSend(wxWindow *parent, int id) m_status_text = new wxStaticText(m_self, wxID_ANY, wxEmptyString); m_status_text->SetForegroundColour(wxColour(107, 107, 107)); m_status_text->SetFont(::Label::Body_13); - m_status_text->SetSize(wxSize(m_self->FromDIP(280), m_self->FromDIP(46))); - m_status_text->SetMaxSize(wxSize(m_self->FromDIP(280), m_self->FromDIP(46))); + m_status_text->SetSize(wxSize(m_self->FromDIP(300), m_self->FromDIP(46))); + m_status_text->SetMaxSize(wxSize(m_self->FromDIP(300), m_self->FromDIP(46))); m_prog = new wxGauge(m_self, wxID_ANY, 100, wxDefaultPosition, wxSize(-1, m_self->FromDIP(6)), wxGA_HORIZONTAL); + m_prog->SetMinSize(wxSize(m_self->FromDIP(300),m_self->FromDIP(6))); m_prog->SetValue(0); - /* block_left = new wxWindow(m_prog, wxID_ANY, wxPoint(0, 0), wxSize(2, m_prog->GetSize().GetHeight() * 2)); - block_left->SetBackgroundColour(wxColour(255, 255, 255)); - block_right = new wxWindow(m_prog, wxID_ANY, wxPoint(m_prog->GetSize().GetWidth() - 2, 0), wxSize(2, m_prog->GetSize().GetHeight() * 2)); - block_right->SetBackgroundColour(wxColour(255, 255, 255));*/ - - m_sizer_bottom->Add(m_prog, 1, wxALIGN_CENTER, 0); - - StateColor btn_bd_white(std::pair(*wxWHITE, StateColor::Disabled), std::pair(wxColour(38, 46, 48), StateColor::Enabled)); + StateColor btn_bd_white(std::pair(*wxWHITE, StateColor::Disabled), std::pair(wxColour(38, 46, 48), StateColor::Enabled)); m_cancelbutton = new Button(m_self, _L("Cancel")); - m_cancelbutton->SetMinSize(wxSize(m_self->FromDIP(64), m_self->FromDIP(24))); + m_cancelbutton->SetMinSize(wxSize(m_self->FromDIP(58), m_self->FromDIP(22))); + m_cancelbutton->SetMaxSize(wxSize(m_self->FromDIP(58), m_self->FromDIP(22))); m_cancelbutton->SetBackgroundColor(wxColour(255, 255, 255)); m_cancelbutton->SetBorderColor(btn_bd_white); m_cancelbutton->SetCornerRadius(m_self->FromDIP(12)); @@ -60,11 +55,19 @@ BBLStatusBarSend::BBLStatusBarSend(wxWindow *parent, int id) m_stext_percent->SetForegroundColour(wxColour(107, 107, 107)); m_stext_percent->SetFont(::Label::Body_13); m_stext_percent->Wrap(-1); - m_sizer_bottom->Add(m_stext_percent, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, 10); + m_hyperlink = new Label(m_self, _L("Check the status of current system services")); + m_hyperlink->SetForegroundColour(0x00AE42); + m_hyperlink->SetFont(::Label::Body_12); + m_hyperlink->Hide(); + m_sizer_bottom->Add(m_prog, 1, wxALIGN_CENTER, 0); + m_sizer_bottom->Add(m_stext_percent, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, 10); + m_sizer_bottom->Add(m_hyperlink, 0, wxALIGN_CENTER, 10); + m_sizer_bottom->Add(0, 0, 1, wxEXPAND, 0); m_sizer_bottom->Add(m_cancelbutton, 0, wxALIGN_CENTER, 0); - m_sizer_body->Add(m_status_text, 0, 0, 0); + + m_sizer_body->Add(m_status_text, 0, wxALL, 0); m_sizer_body->Add(0, 0, 0, wxTOP, 1); m_sizer_body->Add(m_sizer_bottom, 1, wxEXPAND, 0); @@ -123,6 +126,53 @@ void BBLStatusBarSend::clear_percent() m_cancelbutton->Hide(); } +void BBLStatusBarSend::show_networking_test(wxString msg) +{ + std::string url; + std::string country_code = Slic3r::GUI::wxGetApp().app_config->get_country_code(); + + + if (country_code == "US") { + url = "https://status.bambulab.com"; + } + else if (country_code == "CN") { + url = "https://status.bambulab.cn"; + } + else if (country_code == "ENV_CN_DEV") { + url = "https://status.bambu-lab.com"; + } + else if (country_code == "ENV_CN_QA") { + url = "https://status.bambu-lab.com"; + } + else if (country_code == "ENV_CN_PRE") { + url = "https://status.bambu-lab.com"; + } + else { + url = "https://status.bambu-lab.com"; + } + + + m_hyperlink->Bind(wxEVT_LEFT_DOWN, [this, url](auto& e) { + wxLaunchDefaultBrowser(url); + }); + + m_hyperlink->Bind(wxEVT_ENTER_WINDOW, [this, url](auto& e) { + m_hyperlink->SetCursor(wxCURSOR_HAND); + }); + + m_hyperlink->Bind(wxEVT_LEAVE_WINDOW, [this, url](auto& e) { + m_hyperlink->SetCursor(wxCURSOR_ARROW); + }); + + set_status_text(msg); + m_prog->Hide(); + m_stext_percent->Hide(); + m_hyperlink->Show(); + m_cancelbutton->Show(); + m_self->Layout(); + m_sizer->Layout(); +} + void BBLStatusBarSend::show_progress(bool show) { if (show) { @@ -234,7 +284,7 @@ void BBLStatusBarSend::set_status_text(const wxString& txt) //auto txtss = "The printing project is being uploaded... 25%%"; //m_status_text->SetLabelText(txtss); wxString str; - format_text(m_status_text, m_self->FromDIP(280), txt, str); + format_text(m_status_text, m_self->FromDIP(300), txt, str); m_status_text->SetLabelText(str); m_self->Layout(); //if (is_english_text(str)) m_status_text->Wrap(m_self->FromDIP(280)); @@ -281,9 +331,13 @@ bool BBLStatusBarSend::update_status(wxString &msg, bool &was_cancel, int percen void BBLStatusBarSend::reset() { + m_hyperlink->Hide(); + m_prog->Show(); + m_stext_percent->Show(); m_cancelbutton->Show(); - set_status_text(""); m_was_cancelled = false; + + set_status_text(""); set_progress(0); set_percent_text(wxString::Format("%d%%", 0)); } diff --git a/src/slic3r/GUI/BBLStatusBarSend.hpp b/src/slic3r/GUI/BBLStatusBarSend.hpp index 1abe97ee74..190e56e5e8 100644 --- a/src/slic3r/GUI/BBLStatusBarSend.hpp +++ b/src/slic3r/GUI/BBLStatusBarSend.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "Jobs/ProgressIndicator.hpp" #include "Widgets/Label.hpp" @@ -29,6 +30,7 @@ class BBLStatusBarSend : public ProgressIndicator { wxPanel * m_self; // we cheat! It should be the base class but: perl! wxGauge * m_prog; + Label * m_hyperlink; Button * m_cancelbutton; wxStaticText *m_status_text; wxStaticText *m_stext_percent; @@ -49,6 +51,7 @@ public: int get_range() const override; void set_range(int = 100) override; void clear_percent() override; + void show_networking_test(wxString msg) override; void show_progress(bool); void start_busy(int = 100); void stop_busy(); diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 7a32c3d239..ecd528b54d 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -763,7 +763,6 @@ void BackgroundSlicingProcess::finalize_gcode() // collide with the G-code viewer memory mapping of the unprocessed G-code. G-code viewer maps unprocessed G-code, because m_gcode_result // is calculated for the unprocessed G-code and it references lines in the memory mapped G-code file by line numbers. // export_path may be changed by the post-processing script as well if the post processing script decides so, see GH #6042. - //BBS: don't support running post process scripts bool post_processed = run_post_process_scripts(output_path, true, "File", export_path, m_fff_print->full_print_config()); auto remove_post_processed_temp_file = [post_processed, &output_path]() { if (post_processed) diff --git a/src/slic3r/GUI/BindDialog.cpp b/src/slic3r/GUI/BindDialog.cpp index cf1f65b607..7474893873 100644 --- a/src/slic3r/GUI/BindDialog.cpp +++ b/src/slic3r/GUI/BindDialog.cpp @@ -262,8 +262,13 @@ namespace GUI { //check dev_id if (m_machine_info->dev_id.empty()) return; + // update ota version + NetworkAgent* agent = wxGetApp().getAgent(); + if (agent) + agent->track_update_property("dev_ota_version", m_machine_info->get_ota_version()); + m_simplebook->SetSelection(0); - m_bind_job = std::make_shared(m_status_bar, wxGetApp().plater(), m_machine_info->dev_id, m_machine_info->dev_ip); + m_bind_job = std::make_shared(m_status_bar, wxGetApp().plater(), m_machine_info->dev_id, m_machine_info->dev_ip, m_machine_info->bind_sec_link); m_bind_job->set_event_handle(this); m_bind_job->start(); } diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index 24a7388e71..4bc9a82e81 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -20,6 +20,7 @@ double Camera::FrustrumMinZRange = 50.0; double Camera::FrustrumMinNearZ = 100.0; double Camera::FrustrumZMargin = 10.0; double Camera::MaxFovDeg = 60.0; +double Camera::ZoomUnit = 0.1; std::string Camera::get_type_as_string() const { @@ -52,6 +53,12 @@ void Camera::select_next_type() set_type((EType)next); } +void Camera::translate(const Vec3d& displacement) { + if (!displacement.isApprox(Vec3d::Zero())) { + m_view_matrix.translate(-displacement); + } +} + void Camera::set_target(const Vec3d& target) { //BBS do not check validation diff --git a/src/slic3r/GUI/Camera.hpp b/src/slic3r/GUI/Camera.hpp index 99133ff572..3bc1385e89 100644 --- a/src/slic3r/GUI/Camera.hpp +++ b/src/slic3r/GUI/Camera.hpp @@ -22,6 +22,7 @@ struct Camera static double FrustrumMinNearZ; static double FrustrumZMargin; static double MaxFovDeg; + static double ZoomUnit; enum class EType : unsigned char { @@ -67,6 +68,7 @@ public: void enable_update_config_on_type_change(bool enable) { m_update_config_on_type_change_enabled = enable; } + void translate(const Vec3d& displacement); const Vec3d& get_target() const { return m_target; } void set_target(const Vec3d& target); @@ -76,7 +78,7 @@ public: double get_zoom() const { return m_zoom; } double get_inv_zoom() const { assert(m_zoom != 0.0); return 1.0 / m_zoom; } - void update_zoom(double delta_zoom) { set_zoom(m_zoom / (1.0 - std::max(std::min(delta_zoom, 4.0), -4.0) * 0.1)); } + void update_zoom(double delta_zoom) { set_zoom(m_zoom / (1.0 - std::max(std::min(delta_zoom, 4.0), -4.0) * ZoomUnit)); } void set_zoom(double zoom); const BoundingBoxf3& get_scene_box() const { return m_scene_box; } diff --git a/src/slic3r/GUI/CameraUtils.cpp b/src/slic3r/GUI/CameraUtils.cpp new file mode 100644 index 0000000000..99d022e4bc --- /dev/null +++ b/src/slic3r/GUI/CameraUtils.cpp @@ -0,0 +1,131 @@ +#include "CameraUtils.hpp" +#include // projecting points +#include + +#include "slic3r/GUI/3DScene.hpp" // GLVolume +#include "libslic3r/Geometry/ConvexHull.hpp" + +using namespace Slic3r; +using namespace GUI; + +Points CameraUtils::project(const Camera & camera, + const std::vector &points) +{ + Vec4i viewport(camera.get_viewport().data()); + + // Convert our std::vector to Eigen dynamic matrix. + Eigen::Matrix + pts(points.size(), 3); + for (size_t i = 0; i < points.size(); ++i) + pts.block<1, 3>(i, 0) = points[i]; + + // Get the projections. + Eigen::Matrix projections; + igl::project(pts, camera.get_view_matrix().matrix(), + camera.get_projection_matrix().matrix(), viewport, projections); + + Points result; + result.reserve(points.size()); + int window_height = viewport[3]; + + // convert to points --> loss precision + for (int i = 0; i < projections.rows(); ++i) { + double x = projections(i, 0); + double y = projections(i, 1); + // opposit direction o Y + result.emplace_back(x, window_height - y); + } + return result; +} + +Point CameraUtils::project(const Camera &camera, const Vec3d &point) +{ + // IMPROVE: do it faster when you need it (inspire in project multi point) + return project(camera, std::vector{point}).front(); +} + +Slic3r::Polygon CameraUtils::create_hull2d(const Camera & camera, + const GLVolume &volume) +{ + std::vector vertices; + const TriangleMesh *hull = volume.convex_hull(); + if (hull != nullptr) { + const indexed_triangle_set &its = hull->its; + vertices.reserve(its.vertices.size()); + // cast vector + for (const Vec3f &vertex : its.vertices) + vertices.emplace_back(vertex.cast()); + } else { + // Negative volume doesn't have convex hull so use bounding box + auto bb = volume.bounding_box(); + Vec3d &min = bb.min; + Vec3d &max = bb.max; + vertices = {min, + Vec3d(min.x(), min.y(), max.z()), + Vec3d(min.x(), max.y(), min.z()), + Vec3d(min.x(), max.y(), max.z()), + Vec3d(max.x(), min.y(), min.z()), + Vec3d(max.x(), min.y(), max.z()), + Vec3d(max.x(), max.y(), min.z()), + max}; + } + + const Transform3d &trafoMat = + volume.get_instance_transformation().get_matrix() * + volume.get_volume_transformation().get_matrix(); + for (Vec3d &vertex : vertices) + vertex = trafoMat * vertex.cast(); + + Points vertices_2d = project(camera, vertices); + return Geometry::convex_hull(vertices_2d); +} + +void CameraUtils::ray_from_screen_pos(const Camera &camera, const Vec2d &position, Vec3d &point, Vec3d &direction) { + switch (camera.get_type()) { + case Camera::EType::Ortho: return ray_from_ortho_screen_pos(camera, position, point, direction); + case Camera::EType::Perspective: return ray_from_persp_screen_pos(camera, position, point, direction); + default: break; + } +} + +Vec3d CameraUtils::screen_point(const Camera &camera, const Vec2d &position) +{ + double height = camera.get_viewport().data()[3]; + // Y coordinate has opposit direction + return Vec3d(position.x(), height - position.y(), 0.); +} + +void CameraUtils::ray_from_ortho_screen_pos(const Camera &camera, const Vec2d &position, Vec3d &point, Vec3d &direction) +{ + assert(camera.get_type() == Camera::EType::Ortho); + Matrix4d modelview = camera.get_view_matrix().matrix(); + Matrix4d projection = camera.get_projection_matrix().matrix(); + Vec4i viewport(camera.get_viewport().data()); + igl::unproject(screen_point(camera,position), modelview, projection, viewport, point); + direction = camera.get_dir_forward(); +} +void CameraUtils::ray_from_persp_screen_pos(const Camera &camera, const Vec2d &position, Vec3d &point, Vec3d &direction) +{ + assert(camera.get_type() == Camera::EType::Perspective); + Matrix4d modelview = camera.get_view_matrix().matrix(); + Matrix4d projection = camera.get_projection_matrix().matrix(); + Vec4i viewport(camera.get_viewport().data()); + igl::unproject(screen_point(camera, position), modelview, projection, viewport, point); + direction = point - camera.get_position(); +} + +Vec2d CameraUtils::get_z0_position(const Camera &camera, const Vec2d & coor) +{ + Vec3d p0, dir; + ray_from_screen_pos(camera, coor, p0, dir); + + // is approx zero + if ((fabs(dir.z()) - 1e-4) < 0) + return Vec2d(std::numeric_limits::max(), + std::numeric_limits::max()); + + // find position of ray cross plane(z = 0) + double t = p0.z() / dir.z(); + Vec3d p = p0 - t * dir; + return Vec2d(p.x(), p.y()); +} diff --git a/src/slic3r/GUI/CameraUtils.hpp b/src/slic3r/GUI/CameraUtils.hpp new file mode 100644 index 0000000000..c3e938ec42 --- /dev/null +++ b/src/slic3r/GUI/CameraUtils.hpp @@ -0,0 +1,69 @@ +#ifndef slic3r_CameraUtils_hpp_ +#define slic3r_CameraUtils_hpp_ + +#include "Camera.hpp" +#include "libslic3r/Point.hpp" +namespace Slic3r { +class GLVolume; +} + +namespace Slic3r::GUI { +/// +/// Help divide camera data and camera functions +/// This utility work with camera data by static funtions +/// +class CameraUtils +{ +public: + CameraUtils() = delete; // only static functions + + /// + /// Project point throw camera to 2d coordinate into imgui window + /// + /// Projection params + /// Point to project. + /// projected points by camera into coordinate of camera. + /// x(from left to right), y(from top to bottom) + static Points project(const Camera& camera, const std::vector &points); + static Point project(const Camera& camera, const Vec3d &point); + + /// + /// Create hull around GLVolume in 2d space of camera + /// + /// Projection params + /// Outline by 3d object + /// Polygon around object + static Polygon create_hull2d(const Camera &camera, const GLVolume &volume); + + /// + /// Create ray(point and direction) for screen coordinate + /// + /// Definition of camera + /// Position on screen(aka mouse position) + /// OUT start of ray + /// OUT direction of ray + static void ray_from_screen_pos(const Camera &camera, const Vec2d &position, Vec3d &point, Vec3d &direction); + static void ray_from_ortho_screen_pos(const Camera &camera, const Vec2d &position, Vec3d &point, Vec3d &direction); + static void ray_from_persp_screen_pos(const Camera &camera, const Vec2d &position, Vec3d &point, Vec3d &direction); + + /// + /// Unproject mouse coordinate to get position in space where z coor is zero + /// Platter surface should be in z == 0 + /// + /// Projection params + /// Mouse position + /// Position on platter under mouse + static Vec2d get_z0_position(const Camera &camera, const Vec2d &coor); + + /// + /// Create 3d screen point from 2d position + /// + /// Define camera viewport + /// Position on screen(aka mouse position) + /// Point represented screen coor in 3d + static Vec3d screen_point(const Camera &camera, const Vec2d &position); + +}; +} // Slic3r::GUI + +#endif /* slic3r_CameraUtils_hpp_ */ diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index f1cfea6d63..cef3bc24c9 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -283,7 +283,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con ! config->opt_bool("detect_thin_wall") && config->opt_enum("timelapse_type") == TimelapseType::tlTraditional)) { - wxString msg_text = _(L("Spiral mode only works when wall loops is 1, support is disabled, top shell layers is 0, sparse infill density is 0 and timelapse type is traditional")); + wxString msg_text = _(L("Spiral mode only works when wall loops is 1, support is disabled, top shell layers is 0, sparse infill density is 0 and timelapse type is traditional.")); if (is_global_config) msg_text += "\n\n" + _(L("Change these settings automatically? \n" "Yes - Change these settings and enable spiral mode automatically\n" @@ -322,6 +322,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con } //BBS + /* if (config->opt_enum("wall_generator") == PerimeterGeneratorType::Arachne && config->opt_bool("overhang_speed_classic")) { @@ -350,7 +351,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con } is_msg_dlg_already_exist = false; } - + */ // BBS int filament_cnt = wxGetApp().preset_bundle->filament_presets.size(); #if 0 @@ -606,7 +607,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co "bridge_no_support", "max_bridge_length", "support_top_z_distance", "support_bottom_z_distance", //BBS: add more support params to dependent of enable_support "support_type", "support_on_build_plate_only", "support_critical_regions_only", - "support_object_xy_distance", "independent_support_layer_height"}) + "support_object_xy_distance"/*, "independent_support_layer_height"*/}) toggle_field(el, have_support_material); toggle_field("support_threshold_angle", have_support_material && is_auto(support_type)); //toggle_field("support_closing_radius", have_support_material && support_style == smsSnug); @@ -663,6 +664,9 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co for (auto el : {"flush_into_infill", "flush_into_support", "flush_into_objects"}) toggle_field(el, have_prime_tower); + // BBS: MusangKing - Hide "Independent support layer height" option + toggle_line("independent_support_layer_height", have_support_material && !have_prime_tower); + bool have_avoid_crossing_perimeters = config->opt_bool("reduce_crossing_wall"); toggle_line("max_travel_detour_distance", have_avoid_crossing_perimeters); @@ -678,10 +682,6 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co for (auto el : { "fuzzy_skin_thickness", "fuzzy_skin_point_distance"}) toggle_line(el, has_fuzzy_skin); - // C11 printer is not support smooth timelapse - std::string str_preset_type = preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle); - toggle_field("timelapse_type", str_preset_type != "C11"); - bool have_arachne = config->opt_enum("wall_generator") == PerimeterGeneratorType::Arachne; for (auto el : { "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle", "min_feature_size", "min_bead_width", "wall_distribution_count" }) diff --git a/src/slic3r/GUI/DeviceManager.cpp b/src/slic3r/GUI/DeviceManager.cpp index 926242061c..6f619ec8eb 100644 --- a/src/slic3r/GUI/DeviceManager.cpp +++ b/src/slic3r/GUI/DeviceManager.cpp @@ -186,9 +186,9 @@ bool AmsTray::is_unset_third_filament() std::string AmsTray::get_display_filament_type() { if (type == "PLA-S") - return "Support W"; + return "Sup.PLA"; else if (type == "PA-S") - return "Support G"; + return "Sup.PA"; else return type; return type; @@ -196,7 +196,11 @@ std::string AmsTray::get_display_filament_type() std::string AmsTray::get_filament_type() { - if (type == "Support W") { + if (type == "Sup.PLA") { + return "PLA-S"; + } else if (type == "Sup.PA") { + return "PA-S"; + } else if (type == "Support W") { return "PLA-S"; } else if (type == "Support G") { return "PA-S"; @@ -556,9 +560,19 @@ AmsTray *MachineObject::get_curr_tray() Ams* curr_ams = get_curr_Ams(); if (!curr_ams) return nullptr; - auto it = curr_ams->trayList.find(m_tray_now); - if (it != curr_ams->trayList.end()) - return it->second; + try { + int tray_index = atoi(m_tray_now.c_str()); + int ams_index = atoi(curr_ams->id.c_str()); + + std::string tray_now_index = std::to_string(tray_index - ams_index * 4); + auto it = curr_ams->trayList.find(tray_now_index); + if (it != curr_ams->trayList.end()) + return it->second; + } + catch (...) { + ; + } + return nullptr; } @@ -1369,10 +1383,18 @@ void MachineObject::parse_version_func() is_support_ai_monitoring = true; is_support_ams_humidity = true; } - local_use_ssl = ota_version->second.sw_ver.compare("01.03.01.04") >= 0; + if (firmware_type == PrinterFirmwareType::FIRMWARE_TYPE_ENGINEER) + local_use_ssl = false; + else { + local_use_ssl = ota_version->second.sw_ver.compare("01.03.01.04") >= 0; + } } } else if (printer_type == "C11") { - local_use_ssl = true; + if (firmware_type == PrinterFirmwareType::FIRMWARE_TYPE_ENGINEER) + local_use_ssl = false; + else { + local_use_ssl = true; + } is_cloud_print_only = true; if (ota_version != module_vers.end()) { is_support_send_to_sdcard = ota_version->second.sw_ver.compare("01.02.00.00") >= 0; @@ -1383,6 +1405,14 @@ void MachineObject::parse_version_func() } } +bool MachineObject::is_studio_cmd(int sequence_id) +{ + if (sequence_id >= START_SEQ_ID && sequence_id < END_SEQ_ID) { + return true; + } + return false; +} + int MachineObject::command_get_version(bool with_retry) { BOOST_LOG_TRIVIAL(info) << "command_get_version"; @@ -1433,6 +1463,18 @@ int MachineObject::command_pushing(std::string cmd) return -1; } +int MachineObject::command_clean_print_error(std::string subtask_id, int print_error) +{ + BOOST_LOG_TRIVIAL(info) << "command_clean_print_error, id = " << subtask_id; + json j; + j["print"]["command"] = "clean_print_error"; + j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + j["print"]["subtask_id"] = subtask_id; + j["print"]["print_error"] = print_error; + + return this->publish_json(j.dump()); +} + int MachineObject::command_upgrade_confirm() { BOOST_LOG_TRIVIAL(info) << "command_upgrade_confirm"; @@ -1850,8 +1892,8 @@ int MachineObject::command_start_calibration(bool vibration, bool bed_leveling, int MachineObject::command_unload_filament() { - if (printer_type == "BL-P001" - || printer_type == "BL-P002") { + if ((printer_type == "BL-P001" || printer_type == "BL-P002") + && !this->is_function_supported(PrinterFunction::FUNC_VIRTUAL_TYAY)) { // fixed gcode file json j; j["print"]["command"] = "gcode_file"; @@ -1859,7 +1901,10 @@ int MachineObject::command_unload_filament() j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); return this->publish_json(j.dump()); } - else if (printer_type == "C11") { + else if (printer_type == "C11" + || ((printer_type == "BL-P001" || printer_type == "BL-P002") + && this->is_function_supported(PrinterFunction::FUNC_VIRTUAL_TYAY)) + ) { std::string gcode = DeviceManager::load_gcode(printer_type, "ams_unload.gcode"); if (gcode.empty()) { return -1; @@ -2245,6 +2290,14 @@ bool MachineObject::is_function_supported(PrinterFunction func) case FUNC_PRINT_ALL: func_name = "FUNC_PRINT_ALL"; break; + case FUNC_VIRTUAL_TYAY: + if (!ams_support_virtual_tray) + return false; + func_name = "FUNC_VIRTUAL_TYAY"; + break; + case FUNC_FILAMENT_BACKUP: + func_name = "FUNC_FILAMENT_BACKUP"; + break; default: return true; } @@ -2345,6 +2398,18 @@ int MachineObject::parse_json(std::string payload) if (j.contains("print")) { json jj = j["print"]; + int sequence_id = 0; + if (jj.contains("sequence_id")) { + if (jj["sequence_id"].is_string()) { + std::string str_seq = jj["sequence_id"].get(); + try { + sequence_id = stoi(str_seq); + } + catch(...) { + ; + } + } + } if (jj.contains("command")) { if (jj["command"].get() == "push_status") { m_push_count++; @@ -2459,6 +2524,11 @@ int MachineObject::parse_json(std::string payload) if (jj.contains("gcode_state")) { this->set_print_state(jj["gcode_state"].get()); } + if (jj.contains("queue_number")) { + this->queue_number = jj["queue_number"].get(); + } else { + this->queue_number = 0; + } if (jj.contains("task_id")) { this->task_id_ = jj["task_id"].get(); @@ -2619,6 +2689,21 @@ int MachineObject::parse_json(std::string payload) ; } + /*get filam_bak*/ + try { + if (jj.contains("filam_bak")) { + filam_bak.clear(); + if (jj["filam_bak"].is_array()) { + for (auto it = jj["filam_bak"].begin(); it != jj["filam_bak"].end(); it++) { + filam_bak.push_back(it.value().get()); + } + } + } + } + catch (...) { + ; + } + /* get fimware type */ try { if (jj.contains("lifecycle")) { @@ -3116,6 +3201,16 @@ int MachineObject::parse_json(std::string payload) } else { curr_tray->color = ""; } + + curr_tray->cols.clear(); + if (tray_it->contains("cols")) { + if ((*tray_it)["cols"].is_array()) { + for (auto it = (*tray_it)["cols"].begin(); it != (*tray_it)["cols"].end(); it++) { + curr_tray->cols.push_back(it.value().get()); + } + } + } + if (tray_it->contains("remain")) { curr_tray->remain = (*tray_it)["remain"].get(); } else { @@ -3264,6 +3359,16 @@ int MachineObject::parse_json(std::string payload) else { vt_tray.color = ""; } + + vt_tray.cols.clear(); + if (jj["vt_tray"].contains("cols")) { + if (jj["vt_tray"].is_array()) { + for (auto it = jj["vt_tray"].begin(); it != jj["vt_tray"].end(); it++) { + vt_tray.cols.push_back(it.value().get()); + } + } + } + if (jj["vt_tray"].contains("remain")) { vt_tray.remain = jj["vt_tray"]["remain"].get(); } @@ -3283,9 +3388,23 @@ int MachineObject::parse_json(std::string payload) } else if (jj["command"].get() == "gcode_line") { //ack of gcode_line BOOST_LOG_TRIVIAL(debug) << "parse_json, ack of gcode_line = " << j.dump(4); + if (m_agent && is_studio_cmd(sequence_id)) { + json t; + t["dev_id"] = this->dev_id; + t["signal"] = this->wifi_signal; + m_agent->track_event("ack_cmd_gcode_line", t.dump()); + } } else if (jj["command"].get() == "project_file") { //ack of project file BOOST_LOG_TRIVIAL(debug) << "parse_json, ack of project_file = " << j.dump(4); + + if (m_agent && is_studio_cmd(sequence_id)) { + json t; + t["dev_id"] = this->dev_id; + t["signal"] = this->wifi_signal; + m_agent->track_event("ack_cmd_project_file", t.dump()); + } + std::string result; if (jj.contains("result")) { result = jj["result"].get(); @@ -3324,6 +3443,16 @@ int MachineObject::parse_json(std::string payload) tray_it->second->nozzle_temp_min = std::to_string(jj["nozzle_temp_min"].get()); tray_it->second->type = jj["tray_type"].get(); tray_it->second->color = jj["tray_color"].get(); + + /*tray_it->second->cols.clear(); + if (jj.contains("cols")) { + if (jj["cols"].is_array()) { + for (auto it = jj["cols"].begin(); it != jj["cols"].end(); it++) { + tray_it->second->cols.push_back(it.value().get()); + } + } + }*/ + tray_it->second->setting_id = jj["tray_info_idx"].get(); // delay update tray_it->second->set_hold_count(); @@ -3451,6 +3580,7 @@ int MachineObject::parse_json(std::string payload) module_vers.emplace(ver_info.name, ver_info); } parse_version_func(); + bool get_version_result = true; if (j["info"].contains("result")) if (j["info"]["result"].get() == "fail") @@ -3542,8 +3672,13 @@ int MachineObject::publish_gcode(std::string gcode_str) j["print"]["param"] = gcode_str; j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); - if (m_agent) + if (m_agent) { j["print"]["user_id"] = m_agent->get_user_id(); + json t; + t["dev_id"] = this->dev_id; + t["signal"] = this->wifi_signal; + m_agent->track_event("cmd_gcode_line", t.dump()); + } return publish_json(j.dump()); } @@ -3765,14 +3900,19 @@ void DeviceManager::on_machine_alive(std::string json_str) std::string printer_signal = j["dev_signal"].get(); std::string connect_type = j["connect_type"].get(); std::string bind_state = j["bind_state"].get(); + std::string sec_link = ""; + if (j.contains("sec_link")) { + sec_link = j["sec_link"].get(); + } MachineObject* obj; /* update userMachineList info */ auto it = userMachineList.find(dev_id); if (it != userMachineList.end()) { - it->second->dev_ip = dev_ip; - it->second->bind_state = bind_state; + it->second->dev_ip = dev_ip; + it->second->bind_state = bind_state; + it->second->bind_sec_link = sec_link; } /* update localMachineList */ @@ -3786,9 +3926,10 @@ void DeviceManager::on_machine_alive(std::string json_str) obj->dev_ip = dev_ip; /* ip changed reconnect mqtt */ } - obj->wifi_signal = printer_signal; - obj->dev_connection_type = connect_type; - obj->bind_state = bind_state; + obj->wifi_signal = printer_signal; + obj->dev_connection_type= connect_type; + obj->bind_state = bind_state; + obj->bind_sec_link = sec_link; obj->printer_type = MachineObject::parse_printer_type(printer_type_str); // U0 firmware @@ -3811,6 +3952,7 @@ void DeviceManager::on_machine_alive(std::string json_str) obj->wifi_signal = printer_signal; obj->dev_connection_type = connect_type; obj->bind_state = bind_state; + obj->bind_sec_link = sec_link; //load access code AppConfig* config = Slic3r::GUI::wxGetApp().app_config; @@ -3956,7 +4098,7 @@ void DeviceManager::clean_user_info() userMachineList.clear(); } -bool DeviceManager::set_selected_machine(std::string dev_id) +bool DeviceManager::set_selected_machine(std::string dev_id, bool need_disconnect) { BOOST_LOG_TRIVIAL(info) << "set_selected_machine=" << dev_id; auto my_machine_list = get_my_machine_list(); @@ -3966,9 +4108,10 @@ bool DeviceManager::set_selected_machine(std::string dev_id) auto last_selected = my_machine_list.find(selected_machine); if (last_selected != my_machine_list.end()) { if (last_selected->second->connection_type() == "lan") { - if (last_selected->second->is_connecting()) + if (last_selected->second->is_connecting() && !need_disconnect) return false; - m_agent->disconnect_printer(); + + if (!need_disconnect) {m_agent->disconnect_printer();} } } @@ -3982,7 +4125,7 @@ bool DeviceManager::set_selected_machine(std::string dev_id) } else { // lan mode printer reconnect printer if (m_agent) { - m_agent->disconnect_printer(); + if (!need_disconnect) {m_agent->disconnect_printer();} it->second->reset(); it->second->connect(); it->second->set_lan_mode_connection_state(true); diff --git a/src/slic3r/GUI/DeviceManager.hpp b/src/slic3r/GUI/DeviceManager.hpp index 13c066f945..df13853e73 100644 --- a/src/slic3r/GUI/DeviceManager.hpp +++ b/src/slic3r/GUI/DeviceManager.hpp @@ -33,6 +33,8 @@ #define GET_VERSION_RETRYS 10 #define RETRY_INTERNAL 2000 #define VIRTUAL_TRAY_ID 254 +#define START_SEQ_ID 20000 +#define END_SEQ_ID 30000 inline int correct_filament_temperature(int filament_temp) { @@ -89,7 +91,9 @@ enum PrinterFunction { FUNC_AUTO_SWITCH_FILAMENT, FUNC_CHAMBER_FAN, FUNC_EXTRUSION_CALI, + FUNC_VIRTUAL_TYAY, FUNC_PRINT_ALL, + FUNC_FILAMENT_BACKUP, FUNC_MAX }; @@ -191,6 +195,7 @@ public: std::string type; std::string sub_brands; std::string color; + std::vector cols; std::string weight; std::string diameter; std::string temp; @@ -389,7 +394,7 @@ public: }; /* static members and functions */ - static inline int m_sequence_id = 20000; + static inline int m_sequence_id = START_SEQ_ID; static std::string parse_printer_type(std::string type_str); static std::string get_preset_printer_model_name(std::string printer_type); static std::string get_preset_printer_thumbnail_img(std::string printer_type); @@ -430,9 +435,12 @@ public: std::string get_printer_thumbnail_img_str(); std::string product_name; // set by iot service, get /user/print + std::vector filam_bak; + std::string bind_user_name; std::string bind_user_id; std::string bind_state; /* free | occupied */ + std::string bind_sec_link; bool is_avaliable() { return bind_state == "free"; } time_t last_alive; bool m_is_online; @@ -605,7 +613,8 @@ public: void parse_status(int flag); /* printing status */ - std::string print_status; /* enum string: FINISH, RUNNING, PAUSE, INIT, FAILED */ + std::string print_status; /* enum string: FINISH, SLICING, RUNNING, PAUSE, INIT, FAILED */ + int queue_number = 0; std::string iot_print_status; /* iot */ PrintingSpeedLevel printing_speed_lvl; int printing_speed_mag = 100; @@ -682,10 +691,12 @@ public: ~MachineObject(); void parse_version_func(); + bool is_studio_cmd(int seq); /* command commands */ int command_get_version(bool with_retry = true); int command_request_push_all(); int command_pushing(std::string cmd); + int command_clean_print_error(std::string task_id, int print_error); /* command upgrade */ int command_upgrade_confirm(); @@ -816,7 +827,7 @@ public: void erase_user_machine(std::string dev_id); void clean_user_info(); - bool set_selected_machine(std::string dev_id); + bool set_selected_machine(std::string dev_id, bool need_disconnect = false); MachineObject* get_selected_machine(); /* return machine has access code and user machine if login*/ diff --git a/src/slic3r/GUI/ExtraRenderers.cpp b/src/slic3r/GUI/ExtraRenderers.cpp index 7f77c0a4ce..7b1dafb352 100644 --- a/src/slic3r/GUI/ExtraRenderers.cpp +++ b/src/slic3r/GUI/ExtraRenderers.cpp @@ -216,6 +216,7 @@ wxWindow* BitmapTextRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelRec position, labelRect.GetSize(), wxTE_PROCESS_ENTER); text_editor->SetInsertionPointEnd(); text_editor->SelectAll(); + text_editor->SetBackgroundColour(parent->GetBackgroundColour()); return text_editor; } diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 8818f5fd9d..a240ed3e1a 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -178,7 +178,9 @@ void GCodeViewer::InstanceVBuffer::Ranges::reset() void GCodeViewer::InstanceVBuffer::reset() { s_ids.clear(); + s_ids.shrink_to_fit(); buffer.clear(); + buffer.shrink_to_fit(); render_ranges.reset(); } @@ -510,14 +512,14 @@ void GCodeViewer::SequentialView::Marker::render(int canvas_width, int canvas_he imgui.pop_toolbar_style(); } -void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const std::string& filename, std::vector &&lines_ends) +void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const std::string& filename, const std::vector &lines_ends) { assert(! m_file.is_open()); if (m_file.is_open()) return; m_filename = filename; - m_lines_ends = std::move(lines_ends); + m_lines_ends = lines_ends; m_selected_line_id = 0; m_last_lines_size = 0; @@ -627,7 +629,7 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, f imgui.set_next_window_pos(right, top, ImGuiCond_Always, 1.0f, 0.0f); imgui.set_next_window_size(0.0f, wnd_height, ImGuiCond_Always); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); - ImGui::SetNextWindowBgAlpha(0.6f); + ImGui::SetNextWindowBgAlpha(0.8f); imgui.begin(std::string("G-code"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); // center the text in the window by pushing down the first line @@ -909,6 +911,7 @@ void GCodeViewer::init(ConfigOptionMode mode, PresetBundle* preset_bundle) void GCodeViewer::on_change_color_mode(bool is_dark) { m_is_dark = is_dark; m_sequential_view.marker.on_change_color_mode(m_is_dark); + m_sequential_view.gcode_window.on_change_color_mode(m_is_dark); } void GCodeViewer::set_scale(float scale) @@ -1098,9 +1101,20 @@ void GCodeViewer::load(const GCodeProcessorResult& gcode_result, const Print& pr } m_fold = false; + + bool only_gcode_3mf = false; + PartPlate* current_plate = wxGetApp().plater()->get_partplate_list().get_curr_plate(); + bool current_has_print_instances = current_plate->has_printable_instances(); + if (current_plate->is_slice_result_valid() && wxGetApp().model().objects.empty() && !current_has_print_instances) + only_gcode_3mf = true; + m_layers_slider->set_menu_enable(!(only_gcode || only_gcode_3mf)); m_layers_slider->set_as_dirty(); m_moves_slider->set_as_dirty(); + //BBS + m_conflict_result = gcode_result.conflict_result; + if (m_conflict_result) { m_conflict_result.value().layer = m_layers.get_l_at(m_conflict_result.value()._height); } + //BBS: add mutex for protection of gcode result gcode_result.unlock(); //BBS: add logs @@ -1230,6 +1244,7 @@ void GCodeViewer::reset() m_moves_count = 0; m_ssid_to_moveid_map.clear(); + m_ssid_to_moveid_map.shrink_to_fit(); for (TBuffer& buffer : m_buffers) { buffer.reset(); } @@ -1800,7 +1815,10 @@ void GCodeViewer::update_layers_slider_mode() void GCodeViewer::update_marker_curr_move() { if ((int)m_last_result_id != -1) { auto it = std::find_if(m_gcode_result->moves.begin(), m_gcode_result->moves.end(), [this](auto move) { - return move.gcode_id == static_cast(m_sequential_view.gcode_ids[m_sequential_view.current.last]); + if (m_sequential_view.current.last < m_sequential_view.gcode_ids.size() && m_sequential_view.current.last >= 0) { + return move.gcode_id == static_cast(m_sequential_view.gcode_ids[m_sequential_view.current.last]); + } + return false; }); if (it != m_gcode_result->moves.end()) m_sequential_view.marker.update_curr_move(*it); @@ -3183,19 +3201,8 @@ void GCodeViewer::load_shells(const Print& print, bool initialized, bool force_p } if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptFFF) { - // BBS - // adds wipe tower's volume - std::vector print_extruders; - for (auto print_obj : print.objects()) { - ModelObject* mo = print_obj->model_object(); - for (ModelVolume* mv : mo->volumes) { - std::vector volume_extruders = mv->get_extruders(); - print_extruders.insert(print_extruders.end(), volume_extruders.begin(), volume_extruders.end()); - } - } - std::sort(print_extruders.begin(), print_extruders.end()); - auto it_end = std::unique(print_extruders.begin(), print_extruders.end()); - print_extruders.resize(std::distance(print_extruders.begin(), it_end)); + // BBS: adds wipe tower's volume + std::vector print_extruders = print.extruders(true); int extruders_count = print_extruders.size(); const double max_z = print.objects()[0]->model_object()->get_model()->bounding_box().max(2); @@ -4363,7 +4370,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv ImGui::PushStyleColor(ImGuiCol_ScrollbarGrab, ImVec4(0.42f, 0.42f, 0.42f, 1.00f)); ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabHovered, ImVec4(0.93f, 0.93f, 0.93f, 1.00f)); ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabActive, ImVec4(0.93f, 0.93f, 0.93f, 1.00f)); - ImGui::SetNextWindowBgAlpha(0.6f); + ImGui::SetNextWindowBgAlpha(0.8f); const float max_height = 0.75f * static_cast(cnv_size.get_height()); const float child_height = 0.3333f * max_height; ImGui::SetNextWindowSizeConstraints({ 0.0f, 0.0f }, { -1.0f, max_height }); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index f9579d1eab..2a3427ddfd 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -492,6 +492,11 @@ class GCodeViewer std::vector& get_endpoints() { return m_endpoints; } double get_z_at(unsigned int id) const { return (id < m_zs.size()) ? m_zs[id] : 0.0; } Endpoints get_endpoints_at(unsigned int id) const { return (id < m_endpoints.size()) ? m_endpoints[id] : Endpoints(); } + int get_l_at(double z) const + { + auto iter = std::upper_bound(m_zs.begin(), m_zs.end(), z); + return std::distance(m_zs.begin(), iter); + } bool operator != (const Layers& other) const { if (m_zs != other.m_zs) @@ -650,6 +655,7 @@ public: std::string parameters; std::string comment; }; + bool m_is_dark = false; bool m_visible{ true }; uint64_t m_selected_line_id{ 0 }; size_t m_last_lines_size{ 0 }; @@ -663,12 +669,15 @@ public: public: GCodeWindow() = default; ~GCodeWindow() { stop_mapping_file(); } - void load_gcode(const std::string& filename, std::vector &&lines_ends); + void load_gcode(const std::string& filename, const std::vector &lines_ends); void reset() { stop_mapping_file(); m_lines_ends.clear(); + m_lines_ends.shrink_to_fit(); m_lines.clear(); + m_lines.shrink_to_fit(); m_filename.clear(); + m_filename.shrink_to_fit(); } void toggle_visibility() { m_visible = !m_visible; } @@ -676,6 +685,7 @@ public: //BBS: GUI refactor: add canvas size //void render(float top, float bottom, uint64_t curr_line_id) const; void render(float top, float bottom, float right, uint64_t curr_line_id) const; + void on_change_color_mode(bool is_dark) { m_is_dark = is_dark; } void stop_mapping_file(); }; @@ -725,6 +735,9 @@ public: Count }; + //BBS + ConflictResultOpt m_conflict_result; + private: std::vector m_plater_extruder; bool m_gl_data_initialized{ false }; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 1078f60402..ecd3d44d33 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -68,6 +68,9 @@ #include #include +#ifndef IMGUI_DEFINE_MATH_OPERATORS +#define IMGUI_DEFINE_MATH_OPERATORS +#endif #include static constexpr const float TRACKBALLSIZE = 0.8f; @@ -679,6 +682,7 @@ GLCanvas3D::Mouse::Mouse() , position(DBL_MAX, DBL_MAX) , scene_position(DBL_MAX, DBL_MAX, DBL_MAX) , ignore_left_up(false) + , ignore_right_up(false) { } @@ -796,8 +800,6 @@ void GLCanvas3D::Labels::render(const std::vector& sorted_ imgui.begin(owner.title, ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); float win_w = ImGui::GetWindowWidth(); - float label_len = imgui.calc_text_size(owner.label).x; - ImGui::SetCursorPosX(0.5f * (win_w - label_len)); ImGui::AlignTextToFramePadding(); imgui.text(owner.label); @@ -1731,6 +1733,44 @@ bool GLCanvas3D::make_current_for_postinit() { return _set_current(); } +Points GLCanvas3D::estimate_wipe_tower_points(int plate_index, bool global) const +{ + PartPlateList & ppl = wxGetApp().plater()->get_partplate_list(); + DynamicPrintConfig &proj_cfg = wxGetApp().preset_bundle->project_config; + auto & print = wxGetApp().plater()->get_partplate_list().get_current_fff_print(); + int plate_count = ppl.get_plate_count(); + float x = dynamic_cast(proj_cfg.option("wipe_tower_x"))->get_at(plate_index); + float y = dynamic_cast(proj_cfg.option("wipe_tower_y"))->get_at(plate_index); + if (plate_index >= plate_count) { plate_index = 0; } + float w = dynamic_cast(m_config->option("prime_tower_width"))->value; + float v = dynamic_cast(m_config->option("prime_volume"))->value; + Vec3d wipe_tower_size = ppl.get_plate(plate_index)->estimate_wipe_tower_size(w, v); + + if (wipe_tower_size(1) == 0) { + // when depth is unavailable (no items on this plate), we have to estimate the depth using the extruder number of all plates + std::set extruder_ids; + if (global) { + auto objs = wxGetApp().obj_list()->objects(); + for (ModelObject *obj : *objs) { + for (ModelVolume *volume : obj->volumes) { + std::vector es = volume->get_extruders(); + extruder_ids.insert(es.begin(), es.end()); + } + } + } else { + PartPlate* pl = ppl.get_plate(plate_index); + std::vector es = pl->get_extruders(); + extruder_ids.insert(es.begin(), es.end()); + } + int extruder_size = extruder_ids.size(); + wipe_tower_size(1) = extruder_size * print.wipe_tower_data(extruder_size).depth + 2 * print.wipe_tower_data().brim_width; + } + Vec3d plate_origin = ppl.get_plate(plate_index)->get_origin(); + Point wt_min_corner{scale_(x), scale_(y)}; + Point wt_max_corner(scale_(x + wipe_tower_size(0)), scale_(y + wipe_tower_size(1))); + return {wt_min_corner, {wt_max_corner.x(), wt_min_corner.y()}, wt_max_corner, {wt_min_corner.x(), wt_max_corner.y()}}; +} + void GLCanvas3D::render(bool only_init) { if (m_in_render) { @@ -1988,24 +2028,25 @@ void GLCanvas3D::render(bool only_init) m_render_stats.increment_fps_counter(); } -void GLCanvas3D::render_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, Camera::EType camera_type) +void GLCanvas3D::render_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, Camera::EType camera_type, bool use_top_view, bool for_picking) { - render_thumbnail(thumbnail_data, w, h, thumbnail_params, m_volumes, camera_type); + render_thumbnail(thumbnail_data, w, h, thumbnail_params, m_volumes, camera_type, use_top_view, for_picking); } -void GLCanvas3D::render_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, const GLVolumeCollection& volumes, Camera::EType camera_type) +void GLCanvas3D::render_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, + const GLVolumeCollection& volumes, Camera::EType camera_type, bool use_top_view, bool for_picking) { - GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); + GLShaderProgram* shader = wxGetApp().get_shader("thumbnail"); ModelObjectPtrs& model_objects = GUI::wxGetApp().model().objects; std::vector> colors = ::get_extruders_colors(); switch (OpenGLManager::get_framebuffers_type()) { case OpenGLManager::EFramebufferType::Arb: { render_thumbnail_framebuffer(thumbnail_data, w, h, thumbnail_params, - wxGetApp().plater()->get_partplate_list(), model_objects, volumes, colors, shader, camera_type); break; } + wxGetApp().plater()->get_partplate_list(), model_objects, volumes, colors, shader, camera_type, use_top_view, for_picking); break; } case OpenGLManager::EFramebufferType::Ext: { render_thumbnail_framebuffer_ext(thumbnail_data, w, h, thumbnail_params, - wxGetApp().plater()->get_partplate_list(), model_objects, volumes, colors, shader, camera_type); break; } + wxGetApp().plater()->get_partplate_list(), model_objects, volumes, colors, shader, camera_type, use_top_view, for_picking); break; } default: { render_thumbnail_legacy(thumbnail_data, w, h, thumbnail_params, wxGetApp().plater()->get_partplate_list(), model_objects, volumes, colors, shader, camera_type); break; } @@ -2572,8 +2613,12 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re if (wt && (timelapse_enabled || filaments_count > 1)) { 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. - if (co != nullptr && co->value == PrintSequence::ByObject && ppl.get_plate(plate_id)->printable_instance_size() != 1) - continue; + PartPlate* part_plate = ppl.get_plate(plate_id); + if (part_plate->get_print_seq() == PrintSequence::ByObject || + (part_plate->get_print_seq() == PrintSequence::ByDefault && co != nullptr && co->value == PrintSequence::ByObject)) { + if (ppl.get_plate(plate_id)->printable_instance_size() != 1) + continue; + } DynamicPrintConfig& proj_cfg = wxGetApp().preset_bundle->project_config; float x = dynamic_cast(proj_cfg.option("wipe_tower_x"))->get_at(plate_id); @@ -2714,6 +2759,7 @@ void GLCanvas3D::load_gcode_preview(const GCodeProcessorResult& gcode_result, co //BBS: always load shell at preview, do this in load_shells //m_gcode_viewer.update_shells_color_by_extruder(m_config); _set_warning_notification_if_needed(EWarning::ToolpathOutside); + _set_warning_notification_if_needed(EWarning::GCodeConflict); } m_gcode_viewer.refresh(gcode_result, str_tool_colors); @@ -2790,6 +2836,11 @@ void GLCanvas3D::bind_event_handlers() m_canvas->Bind(wxEVT_RIGHT_DCLICK, &GLCanvas3D::on_mouse, this); m_canvas->Bind(wxEVT_PAINT, &GLCanvas3D::on_paint, this); m_canvas->Bind(wxEVT_SET_FOCUS, &GLCanvas3D::on_set_focus, this); + m_canvas->Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent& evt) { + ImGui::SetWindowFocus(nullptr); + render(); + evt.Skip(); + }); m_event_handlers_bound = true; m_canvas->Bind(wxEVT_GESTURE_PAN, &GLCanvas3D::on_gesture, this); @@ -3349,27 +3400,27 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) else if (m_gizmos.is_enabled() && !m_selection.is_empty() && m_canvas_type != CanvasAssembleView) { translationProcessor.process(evt); - //switch (keyCode) - //{ - //case WXK_NUMPAD_PAGEUP: case WXK_PAGEUP: - //case WXK_NUMPAD_PAGEDOWN: case WXK_PAGEDOWN: - //{ - // do_rotate(L("Tool Rotate")); - // m_gizmos.update_data(); + switch (keyCode) + { + case WXK_NUMPAD_PAGEUP: case WXK_PAGEUP: + case WXK_NUMPAD_PAGEDOWN: case WXK_PAGEDOWN: + { + do_rotate(L("Tool Rotate")); + m_gizmos.update_data(); - // // BBS - // //wxGetApp().obj_manipul()->set_dirty(); - // // Let the plater know that the dragging finished, so a delayed refresh - // // of the scene with the background processing data should be performed. - // post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED)); - // // updates camera target constraints - // refresh_camera_scene_box(); - // m_dirty = true; + // BBS + //wxGetApp().obj_manipul()->set_dirty(); + // Let the plater know that the dragging finished, so a delayed refresh + // of the scene with the background processing data should be performed. + post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED)); + // updates camera target constraints + refresh_camera_scene_box(); + m_dirty = true; - // break; - //} - //default: { break; } - //} + break; + } + default: { break; } + } } // BBS: add select view logic @@ -3427,22 +3478,22 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) else if (keyCode == WXK_CONTROL) m_dirty = true; else if (m_gizmos.is_enabled() && !m_selection.is_empty() && m_canvas_type != CanvasAssembleView) { -// auto do_rotate = [this](double angle_z_rad) { -// m_selection.start_dragging(); -// m_selection.rotate(Vec3d(0.0, 0.0, angle_z_rad), TransformationType(TransformationType::World_Relative_Joint)); -// m_selection.stop_dragging(); -// m_dirty = true; -//// wxGetApp().obj_manipul()->set_dirty(); -// }; + auto _do_rotate = [this](double angle_z_rad) { + m_selection.start_dragging(); + m_selection.rotate(Vec3d(0.0, 0.0, angle_z_rad), TransformationType(TransformationType::World_Relative_Joint)); + m_selection.stop_dragging(); + m_dirty = true; +// wxGetApp().obj_manipul()->set_dirty(); + }; translationProcessor.process(evt); - //switch (keyCode) - //{ - //case WXK_NUMPAD_PAGEUP: case WXK_PAGEUP: { do_rotate(0.25 * M_PI); break; } - //case WXK_NUMPAD_PAGEDOWN: case WXK_PAGEDOWN: { do_rotate(-0.25 * M_PI); break; } - //default: { break; } - //} + switch (keyCode) + { + case WXK_NUMPAD_PAGEUP: case WXK_PAGEUP: { _do_rotate(0.25 * M_PI); break; } + case WXK_NUMPAD_PAGEDOWN: case WXK_PAGEDOWN: { _do_rotate(-0.25 * M_PI); break; } + default: { break; } + } } else if (!m_gizmos.is_enabled()) { // DoubleSlider navigation in Preview if (m_canvas_type == CanvasPreview) { @@ -3598,7 +3649,22 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) #else double direction_factor = 1.0; #endif - _update_camera_zoom(direction_factor * (double)evt.GetWheelRotation() / (double)evt.GetWheelDelta()); + auto delta = direction_factor * (double)evt.GetWheelRotation() / (double)evt.GetWheelDelta(); + bool zoom_to_mouse = wxGetApp().app_config->get("zoom_to_mouse") == "true"; + if (!zoom_to_mouse) {// zoom to center + _update_camera_zoom(delta); + } + else { + auto cnv_size = get_canvas_size(); + auto screen_center_3d_pos = _mouse_to_3d({ cnv_size.get_width() * 0.5, cnv_size.get_height() * 0.5 }); + auto mouse_3d_pos = _mouse_to_3d({evt.GetX(), evt.GetY()}); + Vec3d displacement = mouse_3d_pos - screen_center_3d_pos; + wxGetApp().plater()->get_camera().translate(displacement); + auto origin_zoom = wxGetApp().plater()->get_camera().get_zoom(); + _update_camera_zoom(delta); + auto new_zoom = wxGetApp().plater()->get_camera().get_zoom(); + wxGetApp().plater()->get_camera().translate((-displacement) / (new_zoom / origin_zoom)); + } } void GLCanvas3D::on_timer(wxTimerEvent& evt) @@ -3747,6 +3813,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_mouse.ignore_left_up = true; m_tooltip.set_in_imgui(false); if (imgui->update_mouse_data(evt)) { + if (evt.LeftDown() && m_canvas != nullptr) + m_canvas->SetFocus(); m_mouse.position = evt.Leaving() ? Vec2d(-1.0, -1.0) : pos.cast(); m_tooltip.set_in_imgui(true); render(); @@ -3874,6 +3942,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) default: { break; } } } + else if (evt.LeftUp() && + m_gizmos.get_current_type() == GLGizmosManager::EType::Scale && + m_gizmos.get_current()->get_state() == GLGizmoBase::EState::On) { + wxGetApp().obj_list()->selection_changed(); + } return; } @@ -3904,7 +3977,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) p = p->GetParent(); auto *top_level_wnd = dynamic_cast(p); if (top_level_wnd && top_level_wnd->IsActive() && !wxGetApp().get_side_menu_popup_status()) - m_canvas->SetFocus(); + ;// m_canvas->SetFocus(); m_mouse.position = pos.cast(); m_tooltip_enabled = false; // 1) forces a frame render to ensure that m_hover_volume_idxs is updated even when the user right clicks while @@ -4162,6 +4235,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) camera.set_target(camera.get_target() + orig - cur_pos); m_dirty = true; + m_mouse.ignore_right_up = true; } m_mouse.drag.start_position_2D = pos; @@ -4251,7 +4325,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) logical_pos = logical_pos.cwiseQuotient(Vec2d(factor, factor)); #endif // ENABLE_RETINA_GL - if (!m_mouse.dragging) { + if (!m_mouse.ignore_right_up) { //BBS post right click event if (!m_hover_plate_idxs.empty()) { post_event(RBtnPlateEvent(EVT_GLCANVAS_PLATE_RIGHT_CLICK, { logical_pos, m_hover_plate_idxs.front() })); @@ -4412,9 +4486,7 @@ void GLCanvas3D::do_move(const std::string& snapshot_type) ModelObject* m = m_model->objects[i.first]; const double shift_z = m->get_instance_min_z(i.second); //BBS: don't call translate if the z is zero - //BBS: removing sinking logic - if (shift_z != 0.0f) { - //if ((current_printer_technology() == ptSLA || shift_z > SINKING_Z_THRESHOLD) && (shift_z != 0.0f)) { + if ((current_printer_technology() == ptSLA || shift_z > SINKING_Z_THRESHOLD) && (shift_z != 0.0f)) { const Vec3d shift(0.0, 0.0, -shift_z); m_selection.translate(i.first, i.second, shift); m->translate_instance(i.second, shift); @@ -4460,9 +4532,8 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type) if (!snapshot_type.empty()) wxGetApp().plater()->take_snapshot(snapshot_type); - //BBS: removing sinking logic // stores current min_z of instances - /*std::map, double> min_zs; + std::map, double> min_zs; for (int i = 0; i < static_cast(m_model->objects.size()); ++i) { const ModelObject* obj = m_model->objects[i]; for (int j = 0; j < static_cast(obj->instances.size()); ++j) { @@ -4475,7 +4546,7 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type) min_zs[{ i, j }] = obj->instance_bounding_box(j).min.z(); } - }*/ + } std::set> done; // keeps track of modified instances @@ -4520,9 +4591,7 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type) //BBS: don't call translate if the z is zero const double shift_z = m->get_instance_min_z(i.second); // leave sinking instances as sinking - //BBS: removing sinking logic - if (shift_z != 0.0f) { - //if ((min_zs.find({ i.first, i.second })->second >= SINKING_Z_THRESHOLD || shift_z > SINKING_Z_THRESHOLD)&&(shift_z != 0.0f)) { + if ((min_zs.find({ i.first, i.second })->second >= SINKING_Z_THRESHOLD || shift_z > SINKING_Z_THRESHOLD)&&(shift_z != 0.0f)) { const Vec3d shift(0.0, 0.0, -shift_z); m_selection.translate(i.first, i.second, shift); m->translate_instance(i.second, shift); @@ -4549,9 +4618,8 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type) if (!snapshot_type.empty()) wxGetApp().plater()->take_snapshot(snapshot_type); - //BBS: removing sinking logic // stores current min_z of instances - /*std::map, double> min_zs; + std::map, double> min_zs; if (!snapshot_type.empty()) { for (int i = 0; i < static_cast(m_model->objects.size()); ++i) { const ModelObject* obj = m_model->objects[i]; @@ -4559,7 +4627,7 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type) min_zs[{ i, j }] = obj->instance_bounding_box(j).min.z(); } } - }*/ + } std::set> done; // keeps track of modified instances @@ -4605,9 +4673,7 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type) //BBS: don't call translate if the z is zero double shift_z = m->get_instance_min_z(i.second); // leave sinking instances as sinking - //BBS: removing sinking logic - if (shift_z != 0.0f) { - //if ((min_zs.empty() || min_zs.find({ i.first, i.second })->second >= SINKING_Z_THRESHOLD || shift_z > SINKING_Z_THRESHOLD) && (shift_z != 0.0f)) { + if ((min_zs.empty() || min_zs.find({ i.first, i.second })->second >= SINKING_Z_THRESHOLD || shift_z > SINKING_Z_THRESHOLD) && (shift_z != 0.0f)) { Vec3d shift(0.0, 0.0, -shift_z); m_selection.translate(i.first, i.second, shift); m->translate_instance(i.second, shift); @@ -4652,9 +4718,8 @@ void GLCanvas3D::do_mirror(const std::string& snapshot_type) if (!snapshot_type.empty()) wxGetApp().plater()->take_snapshot(snapshot_type); - //BBS: removing sinking logic // stores current min_z of instances - /*std::map, double> min_zs; + std::map, double> min_zs; if (!snapshot_type.empty()) { for (int i = 0; i < static_cast(m_model->objects.size()); ++i) { const ModelObject* obj = m_model->objects[i]; @@ -4662,7 +4727,7 @@ void GLCanvas3D::do_mirror(const std::string& snapshot_type) min_zs[{ i, j }] = obj->instance_bounding_box(j).min.z(); } } - }*/ + } std::set> done; // keeps track of modified instances @@ -4705,9 +4770,7 @@ void GLCanvas3D::do_mirror(const std::string& snapshot_type) //BBS: don't call translate if the z is zero double shift_z = m->get_instance_min_z(i.second); // leave sinking instances as sinking - //BBS: removing sinking logic - if (shift_z != 0.0f) { - //if ((min_zs.empty() || min_zs.find({ i.first, i.second })->second >= SINKING_Z_THRESHOLD || shift_z > SINKING_Z_THRESHOLD)&&(shift_z != 0.0f)) { + if ((min_zs.empty() || min_zs.find({ i.first, i.second })->second >= SINKING_Z_THRESHOLD || shift_z > SINKING_Z_THRESHOLD)&&(shift_z != 0.0f)) { Vec3d shift(0.0, 0.0, -shift_z); m_selection.translate(i.first, i.second, shift); m->translate_instance(i.second, shift); @@ -4752,6 +4815,7 @@ void GLCanvas3D::handle_sidebar_focus_event(const std::string& opt_key, bool foc void GLCanvas3D::handle_layers_data_focus_event(const t_layer_height_range range, const EditorType type) { std::string field = "layer_" + std::to_string(type) + "_" + std::to_string(range.first) + "_" + std::to_string(range.second); + m_gizmos.reset_all_states(); handle_sidebar_focus_event(field, true); } @@ -4926,6 +4990,7 @@ void GLCanvas3D::mouse_up_cleanup() m_mouse.set_start_position_2D_as_invalid(); m_mouse.dragging = false; m_mouse.ignore_left_up = false; + m_mouse.ignore_right_up = false; m_dirty = true; if (m_canvas->HasCapture()) @@ -4977,12 +5042,14 @@ void GLCanvas3D::update_sequential_clearance() for (size_t i = 0; i < m_model->objects.size(); ++i) { ModelObject* model_object = m_model->objects[i]; ModelInstance* model_instance0 = model_object->instances.front(); - Polygon hull_2d = offset(model_object->convex_hull_2d(Geometry::assemble_transform({ 0.0, 0.0, model_instance0->get_offset().z() }, model_instance0->get_rotation(), - model_instance0->get_scaling_factor(), model_instance0->get_mirror())), + Polygon hull_no_offset = model_object->convex_hull_2d(Geometry::assemble_transform({ 0.0, 0.0, model_instance0->get_offset().z() }, model_instance0->get_rotation(), + model_instance0->get_scaling_factor(), model_instance0->get_mirror())); + auto tmp = offset(hull_no_offset, // Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects // exactly by satisfying the extruder_clearance_radius, this test will not trigger collision. shrink_factor, - jtRound, mitter_limit).front(); + jtRound, mitter_limit); + Polygon hull_2d = !tmp.empty() ? tmp.front() : hull_no_offset;// tmp may be empty due to clipper's bug, see STUDIO-2452 Pointf3s& cache_hull_2d = m_sequential_print_clearance.m_hull_2d_cache.emplace_back(Pointf3s()); cache_hull_2d.reserve(hull_2d.points.size()); @@ -5393,7 +5460,9 @@ static void debug_output_thumbnail(const ThumbnailData& thumbnail_data) } #endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT -void GLCanvas3D::render_thumbnail_internal(ThumbnailData& thumbnail_data, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector>& extruder_colors, GLShaderProgram* shader, Camera::EType camera_type) +void GLCanvas3D::render_thumbnail_internal(ThumbnailData& thumbnail_data, const ThumbnailsParams& thumbnail_params, + PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector>& extruder_colors, + GLShaderProgram* shader, Camera::EType camera_type, bool use_top_view, bool for_picking) { //BBS modify visible calc function int plate_idx = thumbnail_params.plate_id; @@ -5414,7 +5483,14 @@ void GLCanvas3D::render_thumbnail_internal(ThumbnailData& thumbnail_data, const auto is_visible = [plate_idx, plate_build_volume](const GLVolume& v) { bool ret = v.printable; if (plate_idx >= 0) { - ret &= plate_build_volume.contains(v.transformed_convex_hull_bounding_box()); + bool contained = false; + BoundingBoxf3 plate_bbox = plate_build_volume; + plate_bbox.min(2) = -1e10; + const BoundingBoxf3& volume_bbox = v.transformed_convex_hull_bounding_box(); + if (plate_bbox.contains(volume_bbox) && (volume_bbox.max(2) > 0)) { + contained = true; + } + ret &= contained; } else { ret &= (!v.shader_outside_printer_detection_enabled || !v.is_outside); @@ -5436,7 +5512,7 @@ void GLCanvas3D::render_thumbnail_internal(ThumbnailData& thumbnail_data, const } } - BOOST_LOG_TRIVIAL(info) << boost::format("render_thumbnail: plate_idx %1% volumes size %2%, shader %3%") % plate_idx % visible_volumes.size() %shader; + BOOST_LOG_TRIVIAL(info) << boost::format("render_thumbnail: plate_idx %1% volumes size %2%, shader %3%, use_top_view=%4%, for_picking=%5%") % plate_idx % visible_volumes.size() %shader %use_top_view %for_picking; //BoundingBoxf3 volumes_box = plate_build_volume; BoundingBoxf3 volumes_box; volumes_box.min.z() = 0; @@ -5446,15 +5522,16 @@ void GLCanvas3D::render_thumbnail_internal(ThumbnailData& thumbnail_data, const volumes_box.merge(vol->transformed_bounding_box()); } } + volumes_box.min.z() = -Slic3r::BuildVolume::SceneEpsilon; double width = volumes_box.max.x() - volumes_box.min.x(); double depth = volumes_box.max.y() - volumes_box.min.y(); double height = volumes_box.max.z() - volumes_box.min.z(); - volumes_box.max.x() = volumes_box.max.x() + width * 0.05f; - volumes_box.min.x() = volumes_box.min.x() - width * 0.05f; - volumes_box.max.y() = volumes_box.max.y() + depth * 0.05f; - volumes_box.min.y() = volumes_box.min.y() - depth * 0.05f; - volumes_box.max.z() = volumes_box.max.z() + height * 0.05f; - volumes_box.min.z() = volumes_box.min.z() - height * 0.05f; + volumes_box.max.x() = volumes_box.max.x() + width * 0.1f; + volumes_box.min.x() = volumes_box.min.x() - width * 0.1f; + volumes_box.max.y() = volumes_box.max.y() + depth * 0.1f; + volumes_box.min.y() = volumes_box.min.y() - depth * 0.1f; + volumes_box.max.z() = volumes_box.max.z() + height * 0.1f; + volumes_box.min.z() = volumes_box.min.z() - height * 0.1f; Camera camera; camera.set_type(camera_type); @@ -5466,53 +5543,130 @@ void GLCanvas3D::render_thumbnail_internal(ThumbnailData& thumbnail_data, const //BoundingBoxf3 plate_box = plate->get_bounding_box(false); //plate_box.min.z() = 0.0; //plate_box.max.z() = 0.0; - camera.zoom_to_box(volumes_box); - const Vec3d& target = camera.get_target(); - double distance = camera.get_distance(); - camera.select_view("iso"); + + if (use_top_view) { + float center_x = (plate_build_volume.max(0) + plate_build_volume.min(0))/2; + float center_y = (plate_build_volume.max(1) + plate_build_volume.min(1))/2; + float distance_z = plate_build_volume.max(2) - plate_build_volume.min(2); + Vec3d center(center_x, center_y, 0.f); + double zoom_ratio, scale_x, scale_y; + + scale_x = ((double)thumbnail_data.width)/(plate_build_volume.max(0) - plate_build_volume.min(0)); + scale_y = ((double)thumbnail_data.height)/(plate_build_volume.max(1) - plate_build_volume.min(1)); + zoom_ratio = (scale_x <= scale_y)?scale_x:scale_y; + camera.look_at(center + distance_z * Vec3d::UnitZ(), center, Vec3d::UnitY()); + camera.set_zoom(zoom_ratio); + //camera.select_view("top"); + } + else { + camera.zoom_to_box(volumes_box); + + const Vec3d& target = camera.get_target(); + double distance = camera.get_distance(); + camera.look_at(target - 0.707 * distance * Vec3d::UnitY() + 0.3 * distance * Vec3d::UnitZ(), target, Vec3d::UnitY() + Vec3d::UnitZ()); + } + camera.apply_view_matrix(); camera.apply_projection(plate_build_volume); //GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); - if (shader == nullptr) { - BOOST_LOG_TRIVIAL(info) << boost::format("render_thumbnail: shader is null, return directly"); + if (!for_picking && (shader == nullptr)) { + BOOST_LOG_TRIVIAL(info) << boost::format("render_thumbnail with no picking: shader is null, return directly"); return; } //if (thumbnail_params.transparent_background) - glsafe(::glClearColor(0.2f, 0.2f, 0.2f, 0.0f)); + if (for_picking) + glsafe(::glClearColor(0.f, 0.f, 0.f, 0.f)); + else { + //glsafe(::glClearColor(0.906f, 0.906f, 0.906f, 1.0f)); + //glsafe(::glClearColor(0.50f, 0.5f, 0.5f, 1.0f)); + //glsafe(::glClearColor(0.121568f, 0.121568f, 0.121568f, 1.0f)); + //glsafe(::glClearColor(0.17647f, 0.17647f, 0.17647f, 1.0f)); + glsafe(::glClearColor(0.906f, 0.906f, 0.906f, 1.0f)); + //glsafe(::glClearColor(0.37647f, 0.37647f, 0.37647f, 0.5f)); too lite + //glsafe(::glClearColor(0.23529f, 0.26666f, 0.2745f, 1.0f)); + } glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); glsafe(::glEnable(GL_DEPTH_TEST)); - shader->start_using(); - shader->set_uniform("emission_factor", 0.0f); + if (for_picking) { + //if (OpenGLManager::can_multisample()) + // This flag is often ignored by NVIDIA drivers if rendering into a screen buffer. + // glsafe(::glDisable(GL_MULTISAMPLE)); - for (GLVolume* vol : visible_volumes) { - //BBS set render color for thumbnails - curr_color[0] = vol->color[0]; - curr_color[1] = vol->color[1]; - curr_color[2] = vol->color[2]; - curr_color[3] = vol->color[3]; + glsafe(::glDisable(GL_BLEND)); - shader->set_uniform("uniform_color", curr_color); - //BBS set all volume to orange - //shader->set_uniform("uniform_color", orange); - /*if (plate_idx > 0) { - shader->set_uniform("uniform_color", orange); + static const GLfloat INV_255 = 1.0f / 255.0f; + + // do not cull backfaces to show broken geometry, if any + glsafe(::glDisable(GL_CULL_FACE)); + + //glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); + //glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); + + for (GLVolume* vol : visible_volumes) { + // Object picking mode. Render the object with a color encoding the object index. + // we reserve color = (0,0,0) for occluders (as the printbed) + // so we shift volumes' id by 1 to get the proper color + //BBS: remove the bed picking logic + unsigned int id = vol->model_object_ID; + //unsigned int id = 1 + volume.second.first; + unsigned int r = (id & (0x000000FF << 0)) >> 0; + unsigned int g = (id & (0x000000FF << 8)) >> 8; + unsigned int b = (id & (0x000000FF << 16)) >> 16; + unsigned int a = 0xFF; + glsafe(::glColor4f((GLfloat)r * INV_255, (GLfloat)g * INV_255, (GLfloat)b * INV_255, (GLfloat)a * INV_255)); + /*curr_color[0] = (GLfloat)r * INV_255; + curr_color[1] = (GLfloat)g * INV_255; + curr_color[2] = (GLfloat)b * INV_255; + curr_color[3] = (GLfloat)a * INV_255; + shader->set_uniform("uniform_color", curr_color);*/ + + bool is_active = vol->is_active; + vol->is_active = true; + vol->simple_render(nullptr, model_objects, extruder_colors); + vol->is_active = is_active; } - else { - shader->set_uniform("uniform_color", (vol->printable && !vol->is_outside) ? orange : gray); - }*/ - // the volume may have been deactivated by an active gizmo - bool is_active = vol->is_active; - vol->is_active = true; - vol->simple_render(shader, model_objects, extruder_colors); - vol->is_active = is_active; - } - shader->stop_using(); + //glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); + //glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); + + glsafe(::glEnable(GL_CULL_FACE)); + + //if (OpenGLManager::can_multisample()) + // glsafe(::glEnable(GL_MULTISAMPLE)); + } + else { + shader->start_using(); + shader->set_uniform("emission_factor", 0.1f); + for (GLVolume* vol : visible_volumes) { + //BBS set render color for thumbnails + curr_color[0] = vol->color[0]; + curr_color[1] = vol->color[1]; + curr_color[2] = vol->color[2]; + curr_color[3] = vol->color[3]; + + shader->set_uniform("uniform_color", curr_color); + shader->set_uniform("volume_world_matrix", vol->world_matrix()); + //BBS set all volume to orange + //shader->set_uniform("uniform_color", orange); + /*if (plate_idx > 0) { + shader->set_uniform("uniform_color", orange); + } + else { + shader->set_uniform("uniform_color", (vol->printable && !vol->is_outside) ? orange : gray); + }*/ + // the volume may have been deactivated by an active gizmo + bool is_active = vol->is_active; + vol->is_active = true; + vol->simple_render(shader, model_objects, extruder_colors); + vol->is_active = is_active; + } + shader->stop_using(); + } glsafe(::glDisable(GL_DEPTH_TEST)); @@ -5525,7 +5679,9 @@ void GLCanvas3D::render_thumbnail_internal(ThumbnailData& thumbnail_data, const BOOST_LOG_TRIVIAL(info) << boost::format("render_thumbnail: finished"); } -void GLCanvas3D::render_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector>& extruder_colors, GLShaderProgram* shader, Camera::EType camera_type) +void GLCanvas3D::render_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, + PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector>& extruder_colors, + GLShaderProgram* shader, Camera::EType camera_type, bool use_top_view, bool for_picking) { thumbnail_data.set(w, h); if (!thumbnail_data.is_valid()) @@ -5576,7 +5732,7 @@ void GLCanvas3D::render_thumbnail_framebuffer(ThumbnailData& thumbnail_data, uns glsafe(::glDrawBuffers(1, drawBufs)); if (::glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) { - render_thumbnail_internal(thumbnail_data, thumbnail_params, partplate_list, model_objects, volumes, extruder_colors, shader, camera_type); + render_thumbnail_internal(thumbnail_data, thumbnail_params, partplate_list, model_objects, volumes, extruder_colors, shader, camera_type, use_top_view, for_picking); if (multisample) { GLuint resolve_fbo; @@ -5629,7 +5785,9 @@ void GLCanvas3D::render_thumbnail_framebuffer(ThumbnailData& thumbnail_data, uns BOOST_LOG_TRIVIAL(info) << boost::format("render_thumbnail prepare: finished"); } -void GLCanvas3D::render_thumbnail_framebuffer_ext(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector>& extruder_colors, GLShaderProgram* shader, Camera::EType camera_type) +void GLCanvas3D::render_thumbnail_framebuffer_ext(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, + PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector>& extruder_colors, + GLShaderProgram* shader, Camera::EType camera_type, bool use_top_view, bool for_picking) { thumbnail_data.set(w, h); if (!thumbnail_data.is_valid()) @@ -5679,7 +5837,7 @@ void GLCanvas3D::render_thumbnail_framebuffer_ext(ThumbnailData& thumbnail_data, glsafe(::glDrawBuffers(1, drawBufs)); if (::glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT) { - render_thumbnail_internal(thumbnail_data, thumbnail_params, partplate_list, model_objects, volumes, extruder_colors, shader, camera_type); + render_thumbnail_internal(thumbnail_data, thumbnail_params, partplate_list, model_objects, volumes, extruder_colors, shader, camera_type, use_top_view, for_picking); if (multisample) { GLuint resolve_fbo; @@ -6575,14 +6733,18 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with else m_volumes.set_z_range(-FLT_MAX, FLT_MAX); + GLGizmosManager& gm = get_gizmos_manager(); + GLGizmoBase* current_gizmo = gm.get_current(); if (m_canvas_type == CanvasAssembleView) { m_volumes.set_clipping_plane(m_gizmos.get_assemble_view_clipping_plane().get_data()); } + else if (current_gizmo && !current_gizmo->apply_clipping_plane()) { + m_volumes.set_clipping_plane(ClippingPlane::ClipsNothing().get_data()); + } else { m_volumes.set_clipping_plane(m_camera_clipping_plane.get_data()); } - //BBS: remove sinking logic - //m_volumes.set_show_sinking_contours(! m_gizmos.is_hiding_instances()); + m_volumes.set_show_sinking_contours(! m_gizmos.is_hiding_instances()); GLShaderProgram* shader = wxGetApp().get_shader("gouraud"); ECanvasType canvas_type = this->m_canvas_type; @@ -7207,7 +7369,7 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar() bool is_hovered = false; - m_sel_plate_toolbar.set_icon_size(80.0f * f_scale, 80.0f * f_scale); + m_sel_plate_toolbar.set_icon_size(100.0f * f_scale, 100.0f * f_scale); float button_width = m_sel_plate_toolbar.icon_width; float button_height = m_sel_plate_toolbar.icon_height; @@ -7227,7 +7389,9 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar() ImVec4 button_active = ImVec4(0.12f, 0.56f, 0.92, 1.0f); ImVec4 button_hover = ImVec4(0.67f, 0.67f, 0.67, 1.0f); ImVec4 scroll_col = ImVec4(0.77f, 0.77f, 0.77f, 1.0f); - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.f, 0.f, 0.f, 1.0f)); + //ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.f, 0.f, 0.f, 1.0f)); + //use white text as the background switch to black + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 1.0f, 1.0f)); ImGui::PushStyleColor(ImGuiCol_WindowBg, window_bg); ImGui::PushStyleColor(ImGuiCol_ScrollbarBg, window_bg); ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabActive, scroll_col); @@ -7352,7 +7516,7 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar() if (item->selected) { ImGui::PushStyleColor(ImGuiCol_Button, button_active); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, button_active); - } + } else { ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(128.0f, 128.0f, 128.0f, 0.0f)); ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.42f, 0.42f, 0.42f, 1.0f)); @@ -7558,11 +7722,12 @@ void GLCanvas3D::_render_paint_toolbar() const #if ENABLE_RETINA_GL float f_scale = m_retina_helper->get_scale_factor(); #else - float f_scale = 1.0; + float f_scale = 1.0f; #endif + int em_unit = wxGetApp().em_unit() / 10; + std::vector colors = wxGetApp().plater()->get_extruder_colors_from_plater_config(); int extruder_num = colors.size(); - std::vector filament_text_first_line; std::vector filament_text_second_line; { @@ -7587,65 +7752,108 @@ void GLCanvas3D::_render_paint_toolbar() const } ImGuiWrapper& imgui = *wxGetApp().imgui(); - float canvas_w = float(get_canvas_size().get_width()); - int item_spacing = 8 * wxGetApp().toolbar_icon_scale() * f_scale; - float button_size = GLToolbar::Default_Icons_Size * f_scale * wxGetApp().toolbar_icon_scale() + item_spacing; + const float canvas_w = float(get_canvas_size().get_width()); + const ImVec2 button_size = ImVec2(64.0f, 48.0f) * f_scale * em_unit; + const float spacing = 4.0f * em_unit * f_scale; + const float return_button_margin = 130.0f * em_unit * f_scale; + + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(spacing, spacing)); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(spacing, 0)); + ImGui::PushStyleColor(ImGuiCol_WindowBg, { 0.f, 0.f, 0.f, 0.4f }); imgui.set_next_window_pos(0.5f * canvas_w, 0, ImGuiCond_Always, 0.5f, 0.0f); - imgui.begin(_L("Paint Toolbar"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar); + float constraint_window_width = canvas_w - 2 * return_button_margin; + ImGui::SetNextWindowSizeConstraints({ 0, 0 }, { constraint_window_width, FLT_MAX }); + imgui.begin(_L("Paint Toolbar"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse); + + const float cursor_y = ImGui::GetCursorPosY(); + const ImVec2 arrow_button_size = ImVec2(0.375f * button_size.x, ImGui::GetWindowHeight()); + const ImRect left_arrow_button = ImRect(ImGui::GetCurrentWindow()->Pos, ImGui::GetCurrentWindow()->Pos + arrow_button_size); + const ImRect right_arrow_button = ImRect(ImGui::GetCurrentWindow()->Pos + ImGui::GetWindowSize() - arrow_button_size, ImGui::GetCurrentWindow()->Pos + ImGui::GetWindowSize()); + ImU32 left_arrow_button_color = IM_COL32(0, 0, 0, 0.4f * 255); + ImU32 right_arrow_button_color = IM_COL32(0, 0, 0, 0.4f * 255); + ImU32 arrow_color = IM_COL32(255, 255, 255, 255); + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + ImGuiContext& context = *GImGui; bool disabled = !wxGetApp().plater()->can_fillcolor(); unsigned char rgb[3]; - float cursor_y = ImGui::GetCursorPosY(); for (int i = 0; i < extruder_num; i++) { if (i > 0) - ImGui::SameLine(0.0f, item_spacing); + ImGui::SameLine(); Slic3r::GUI::BitmapCache::parse_color(colors[i], rgb); ImGui::PushStyleColor(ImGuiCol_Button, ImColor(rgb[0], rgb[1], rgb[2]).Value); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImColor(rgb[0], rgb[1], rgb[2]).Value); ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImColor(rgb[0], rgb[1], rgb[2]).Value); if (disabled) ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); - if (ImGui::Button(("##filament_button" + std::to_string(i)).c_str(), ImVec2(button_size, button_size))) { - wxPostEvent(m_canvas, IntEvent(EVT_GLTOOLBAR_FILLCOLOR, i + 1)); + if (ImGui::Button(("##filament_button" + std::to_string(i)).c_str(), button_size)) { + if (!ImGui::IsMouseHoveringRect(left_arrow_button.Min, left_arrow_button.Max) && !ImGui::IsMouseHoveringRect(right_arrow_button.Min, right_arrow_button.Max)) + wxPostEvent(m_canvas, IntEvent(EVT_GLTOOLBAR_FILLCOLOR, i + 1)); } - if (ImGui::IsItemHovered()) { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(ImGui::GetFontSize() * 20.0f); - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.00f, 1.00f, 1.00f, 1.00f)); - ImGui::TextUnformatted((boost::format(_u8L("Shortcut key %1%")) % (i + 1)).str().c_str()); - ImGui::PopStyleColor(1); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); + if (ImGui::IsItemHovered() && i < 9) { + if (!ImGui::IsMouseHoveringRect(left_arrow_button.Min, left_arrow_button.Max) && !ImGui::IsMouseHoveringRect(right_arrow_button.Min, right_arrow_button.Max)) { + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 20.0f * f_scale, 10.0f * f_scale }); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 3.0f * f_scale); + imgui.tooltip(_L("Shortcut Key ") + std::to_string(i + 1), ImGui::GetFontSize() * 20.0f); + ImGui::PopStyleVar(2); + } } ImGui::PopStyleColor(3); if (disabled) ImGui::PopItemFlag(); } - float text_offset_y = 3.0f * f_scale; + const float text_offset_y = 4.0f * em_unit * f_scale; for (int i = 0; i < extruder_num; i++){ Slic3r::GUI::BitmapCache::parse_color(colors[i], rgb); float gray = 0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2]; - ImVec4 text_color = gray < 80 ? ImVec4(255, 255, 255, 255) : ImVec4(0, 0, 0, 255); - + ImVec4 text_color = gray < 80 ? ImVec4(1.0f, 1.0f, 1.0f, 1.0f) : ImVec4(0, 0, 0, 1.0f); + + imgui.push_bold_font(); ImVec2 number_label_size = ImGui::CalcTextSize(std::to_string(i + 1).c_str()); ImGui::SetCursorPosY(cursor_y + text_offset_y); - ImGui::SetCursorPosX(item_spacing + i * (item_spacing + button_size) + (button_size - number_label_size.x) / 2); + ImGui::SetCursorPosX(spacing + i * (spacing + button_size.x) + (button_size.x - number_label_size.x) / 2); ImGui::TextColored(text_color, std::to_string(i + 1).c_str()); + imgui.pop_bold_font(); ImVec2 filament_first_line_label_size = ImGui::CalcTextSize(filament_text_first_line[i].c_str()); ImGui::SetCursorPosY(cursor_y + text_offset_y + number_label_size.y); - ImGui::SetCursorPosX(item_spacing + i * (item_spacing + button_size) + (button_size - filament_first_line_label_size.x) / 2); + ImGui::SetCursorPosX(spacing + i * (spacing + button_size.x) + (button_size.x - filament_first_line_label_size.x) / 2); ImGui::TextColored(text_color, filament_text_first_line[i].c_str()); ImVec2 filament_second_line_label_size = ImGui::CalcTextSize(filament_text_second_line[i].c_str()); ImGui::SetCursorPosY(cursor_y + text_offset_y + number_label_size.y + filament_first_line_label_size.y); - ImGui::SetCursorPosX(item_spacing + i * (item_spacing + button_size) + (button_size - filament_second_line_label_size.x) / 2); + ImGui::SetCursorPosX(spacing + i * (spacing + button_size.x) + (button_size.x - filament_second_line_label_size.x) / 2); ImGui::TextColored(text_color, filament_text_second_line[i].c_str()); } + if (ImGui::GetWindowWidth() == constraint_window_width) { + if (ImGui::IsMouseHoveringRect(left_arrow_button.Min, left_arrow_button.Max)) { + left_arrow_button_color = IM_COL32(0, 0, 0, 0.64f * 255); + if (context.IO.MouseClicked[ImGuiMouseButton_Left]) { + ImGui::SetScrollX(ImGui::GetScrollX() - button_size.x); + imgui.set_requires_extra_frame(); + } + } + draw_list->AddRectFilled(left_arrow_button.Min, left_arrow_button.Max, left_arrow_button_color); + ImGui::BBLRenderArrow(draw_list, left_arrow_button.GetCenter() - ImVec2(draw_list->_Data->FontSize, draw_list->_Data->FontSize) * 0.5f, arrow_color, ImGuiDir_Left, 2.0f); + + if (ImGui::IsMouseHoveringRect(right_arrow_button.Min, right_arrow_button.Max)) { + right_arrow_button_color = IM_COL32(0, 0, 0, 0.64f * 255); + if (context.IO.MouseClicked[ImGuiMouseButton_Left]) { + ImGui::SetScrollX(ImGui::GetScrollX() + button_size.x); + imgui.set_requires_extra_frame(); + } + } + draw_list->AddRectFilled(right_arrow_button.Min, right_arrow_button.Max, right_arrow_button_color); + ImGui::BBLRenderArrow(draw_list, right_arrow_button.GetCenter() - ImVec2(draw_list->_Data->FontSize, draw_list->_Data->FontSize) * 0.5f, arrow_color, ImGuiDir_Right, 2.0f); + } + imgui.end(); + ImGui::PopStyleVar(3); + ImGui::PopStyleColor(); } //BBS @@ -7671,7 +7879,7 @@ void GLCanvas3D::_render_assemble_control() const const float item_spacing = imgui->get_item_spacing().x; ImVec2 window_padding = ImGui::GetStyle().WindowPadding; - imgui->set_next_window_pos(canvas_w / 2, canvas_h - 13.0f * get_scale(), ImGuiCond_Always, 0.5f, 1.0f); + imgui->set_next_window_pos(canvas_w / 2, canvas_h - 10.0f * get_scale(), ImGuiCond_Always, 0.5f, 1.0f); imgui->begin(_L("Assemble Control"), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar); ImGui::AlignTextToFramePadding(); @@ -7740,24 +7948,18 @@ void GLCanvas3D::_render_assemble_info() const ImGuiWrapper* imgui = wxGetApp().imgui(); auto canvas_w = float(get_canvas_size().get_width()); auto canvas_h = float(get_canvas_size().get_height()); - float window_height = 130.0f; float space_size = imgui->get_style_scaling() * 8.0f; float caption_max = imgui->calc_text_size(_L("Total Volume:")).x + 3 * space_size; char buf[3][64]; - float merged_max = 0.0; - for (int i = 0; i < 3; i++) { - ImGui::DataTypeFormatString(buf[i], IM_ARRAYSIZE(buf[i]), ImGuiDataType_Double, (void *) &volumes_bounding_box().size()(i), "%.2f"); - merged_max += ImGui::CalcTextSize(buf[i]).x; - if (i < 2) merged_max += ImGui::CalcTextSize(" x ").x; - } - float window_width = merged_max + caption_max + ImGui::GetFrameHeight(); + ImGuiIO& io = ImGui::GetIO(); ImFont* font = io.Fonts->Fonts[0]; float origScale = font->Scale; font->Scale = 1.2; ImGui::PushFont(font); ImGui::PopFont(); - imgui->set_next_window_pos(canvas_w - window_width, 0.0f, ImGuiCond_Always, 0, 0); + float margin = 10.0f * get_scale(); + imgui->set_next_window_pos(canvas_w - margin, canvas_h - margin, ImGuiCond_Always, 1.0f, 1.0f); ImGuiWrapper::push_toolbar_style(get_scale()); imgui->begin(_L("Assembly Info"), ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse); font->Scale = origScale; @@ -7923,8 +8125,7 @@ void GLCanvas3D::_render_sla_slices() void GLCanvas3D::_render_selection_sidebar_hints() const { - //BBS do not render sidebar hints - //m_selection.render_sidebar_hints(m_sidebar_field, m_gizmos.get_uniform_scaling()); + m_selection.render_sidebar_hints(m_sidebar_field, m_gizmos.get_uniform_scaling()); } void GLCanvas3D::_update_volumes_hover_state() @@ -8654,7 +8855,10 @@ void GLCanvas3D::_set_warning_notification_if_needed(EWarning warning) else { if (wxGetApp().is_editor()) { if (current_printer_technology() != ptSLA) - show = m_gcode_viewer.has_data() && !m_gcode_viewer.is_contained_in_bed(); + if (warning == EWarning::ToolpathOutside) + show = m_gcode_viewer.has_data() && !m_gcode_viewer.is_contained_in_bed(); + else if (warning==EWarning::GCodeConflict) + show = m_gcode_viewer.has_data() && m_gcode_viewer.is_contained_in_bed() && m_gcode_viewer.m_conflict_result.has_value(); } } @@ -8693,6 +8897,18 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) std::string text; ErrorType error = ErrorType::PLATER_WARNING; switch (warning) { + case EWarning::GCodeConflict: { + if (!m_gcode_viewer.m_conflict_result) { break; } + std::string objName1 = m_gcode_viewer.m_conflict_result.value()._objName1; + std::string objName2 = m_gcode_viewer.m_conflict_result.value()._objName2; + double height = m_gcode_viewer.m_conflict_result.value()._height; + int layer = m_gcode_viewer.m_conflict_result.value().layer; + text = (boost::format(_u8L("Conflicts of gcode paths have been found at layer %d, z = %.2lf mm. Please separate the conflicted objects farther (%s <-> %s).")) % layer % + height % objName1 % objName2) + .str(); + error = ErrorType::SLICING_ERROR; + break; + } case EWarning::ObjectOutside: text = _u8L("An object is layed over the boundary of plate."); break; case EWarning::ToolpathOutside: text = _u8L("A G-code path goes beyond the boundary of plate."); error = ErrorType::SLICING_ERROR; break; // BBS: remove _u8L() for SLA @@ -8708,6 +8924,26 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) if (!wxGetApp().plater()) return; auto& notification_manager = *wxGetApp().plater()->get_notification_manager(); + + if (warning == EWarning::GCodeConflict && m_gcode_viewer.m_conflict_result) { + const PrintObject *obj2 = reinterpret_cast(m_gcode_viewer.m_conflict_result.value()._obj2); + auto mo = obj2->model_object(); + ObjectID id = mo->id(); + auto action_fn = [id](wxEvtHandler *) { + auto &objects = wxGetApp().model().objects; + auto iter = id.id ? std::find_if(objects.begin(), objects.end(), [id](auto o) { return o->id() == id; }) : objects.end(); + if (iter != objects.end()) { + wxGetApp().mainframe->select_tab(MainFrame::tp3DEditor); + wxGetApp().obj_list()->select_items({{*iter, nullptr}}); + } + return false; + }; + auto hypertext = _u8L("Jump to"); + hypertext += std::string(" [") + mo->name + "]"; + notification_manager.push_notification(NotificationType::PlaterError, NotificationManager::NotificationLevel::ErrorNotificationLevel, _u8L("ERROR:") + "\n" + text, + hypertext, action_fn); + return; + } switch (error) { case PLATER_WARNING: diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index e1b0787e3d..8beb998384 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -334,6 +334,7 @@ class GLCanvas3D Vec3d scene_position; Drag drag; bool ignore_left_up; + bool ignore_right_up; Mouse(); @@ -371,7 +372,8 @@ class GLCanvas3D ToolpathOutside, SlaSupportsOutside, SomethingNotShown, - ObjectClashed + ObjectClashed, + GCodeConflict }; class RenderStats @@ -832,13 +834,21 @@ public: // printable_only == false -> render also non printable volumes as grayed // parts_only == false -> render also sla support and pad - void render_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, Camera::EType camera_type); - void render_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, const GLVolumeCollection& volumes, Camera::EType camera_type); - static void render_thumbnail_internal(ThumbnailData& thumbnail_data, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector>& extruder_colors, GLShaderProgram* shader, Camera::EType camera_type); + void render_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, + Camera::EType camera_type, bool use_top_view = false, bool for_picking = false); + void render_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, + const GLVolumeCollection& volumes, Camera::EType camera_type, bool use_top_view = false, bool for_picking = false); + static void render_thumbnail_internal(ThumbnailData& thumbnail_data, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, ModelObjectPtrs& model_objects, + const GLVolumeCollection& volumes, std::vector>& extruder_colors, + GLShaderProgram* shader, Camera::EType camera_type, bool use_top_view = false, bool for_picking = false); // render thumbnail using an off-screen framebuffer - static void render_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector>& extruder_colors, GLShaderProgram* shader, Camera::EType camera_type); + static void render_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, + PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector>& extruder_colors, + GLShaderProgram* shader, Camera::EType camera_type, bool use_top_view = false, bool for_picking = false); // render thumbnail using an off-screen framebuffer when GLEW_EXT_framebuffer_object is supported - static void render_thumbnail_framebuffer_ext(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector>& extruder_colors, GLShaderProgram* shader, Camera::EType camera_type); + static void render_thumbnail_framebuffer_ext(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, + PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector>& extruder_colors, + GLShaderProgram* shader, Camera::EType camera_type, bool use_top_view = false, bool for_picking = false); //BBS use gcoder viewer render calibration thumbnails void render_calibration_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params); @@ -1048,6 +1058,9 @@ public: bool make_current_for_postinit(); + //BBS + Points estimate_wipe_tower_points(int plate_index, bool global = true) const; + private: bool _is_shown_on_screen() const; diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index 33e5a09540..107fcb5627 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -35,6 +35,8 @@ std::pair GLShadersManager::init() // used to render bed axes and model, selection hints, gcode sequential view marker model, preview shells, options in gcode preview valid &= append_shader("gouraud_light", { "gouraud_light.vs", "gouraud_light.fs" }); + //used to render thumbnail + valid &= append_shader("thumbnail", { "thumbnail.vs", "thumbnail.fs" }); // used to render first layer for calibration valid &= append_shader("cali", { "cali.vs", "cali.fs"}); // used to render printbed @@ -44,6 +46,7 @@ std::pair GLShadersManager::init() valid &= append_shader("gouraud_light_instanced", { "gouraud_light_instanced.vs", "gouraud_light_instanced.fs" }); // used to render extrusion and travel paths as lines in gcode preview valid &= append_shader("toolpaths_lines", { "toolpaths_lines.vs", "toolpaths_lines.fs" }); + // used to render objects in 3d editor //if (GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 0)) { if (0) { diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index 8c90f8413c..5d4455fcee 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -84,7 +84,7 @@ const std::string& shortkey_ctrl_prefix() #ifdef __APPLE__ "⌘+" #else - "Ctrl+" + _u8L("Ctrl+") #endif ; return str; @@ -221,8 +221,10 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt void show_error(wxWindow* parent, const wxString& message, bool monospaced_font) { - ErrorDialog msg(parent, message, monospaced_font); - msg.ShowModal(); + wxGetApp().CallAfter([=] { + ErrorDialog msg(parent, message, monospaced_font); + msg.ShowModal(); + }); } void show_error(wxWindow* parent, const char* message, bool monospaced_font) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 3d96dd9928..6d3c389768 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -61,6 +61,7 @@ #include "../Utils/Process.hpp" #include "../Utils/MacDarkMode.hpp" #include "../Utils/Http.hpp" +#include "../Utils/UndoRedo.hpp" #include "slic3r/Config/Snapshot.hpp" #include "Preferences.hpp" #include "Tab.hpp" @@ -951,28 +952,45 @@ static void generic_exception_handle() } catch (const std::bad_alloc& ex) { // bad_alloc in main thread is most likely fatal. Report immediately to the user (wxLogError would be delayed) // and terminate the app so it is at least certain to happen now. + BOOST_LOG_TRIVIAL(error) << boost::format("std::bad_alloc exception: %1%") % ex.what(); + flush_logs(); wxString errmsg = wxString::Format(_L("OrcaSlicer will terminate because of running out of memory." "It may be a bug. It will be appreciated if you report the issue to our team.")); wxMessageBox(errmsg + "\n\n" + wxString(ex.what()), _L("Fatal error"), wxOK | wxICON_ERROR); - BOOST_LOG_TRIVIAL(error) << boost::format("std::bad_alloc exception: %1%") % ex.what(); std::terminate(); //throw; } catch (const boost::io::bad_format_string& ex) { + BOOST_LOG_TRIVIAL(error) << boost::format("Uncaught exception: %1%") % ex.what(); + flush_logs(); wxString errmsg = _L("OrcaSlicer will terminate because of a localization error. " "It will be appreciated if you report the specific scenario this issue happened."); wxMessageBox(errmsg + "\n\n" + wxString(ex.what()), _L("Critical error"), wxOK | wxICON_ERROR); - BOOST_LOG_TRIVIAL(error) << boost::format("Uncaught exception: %1%") % ex.what(); std::terminate(); //throw; } catch (const std::exception& ex) { wxLogError(format_wxstr(_L("OrcaSlicer got an unhandled exception: %1%"), ex.what())); BOOST_LOG_TRIVIAL(error) << boost::format("Uncaught exception: %1%") % ex.what(); + flush_logs(); throw; } //#endif } +static std::vector split_str(const std::string& src, const std::string& separator) +{ + size_t pos; + size_t start_pos = 0; + vector result_str; + while ((pos = src.find(separator, start_pos)) != string::npos) + { + result_str.emplace_back(src.substr(start_pos, pos - start_pos)); + start_pos = pos + separator.size(); + } + result_str.emplace_back(src.substr(start_pos, src.size() - pos - separator.size())); + return result_str; +} + void GUI_App::post_init() { assert(initialized()); @@ -981,29 +999,55 @@ void GUI_App::post_init() bool switch_to_3d = false; if (!this->init_params->input_files.empty()) { + + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", init with input files, size %1%, input_gcode %2%") %this->init_params->input_files.size() %this->init_params->input_gcode; - switch_to_3d = true; - if (this->init_params->input_gcode) { - mainframe->select_tab(size_t(MainFrame::tp3DEditor)); - plater_->select_view_3D("3D"); - this->plater()->load_gcode(from_u8(this->init_params->input_files.front())); + + + + if (this->init_params->input_files.size() == 1 && + boost::starts_with(this->init_params->input_files.front(), "bambustudio://open")) { + auto input_str_arr = split_str(this->init_params->input_files.front(), "bambustudio://open/?file="); + + std::string download_origin_url; + for (auto input_str:input_str_arr) { + if (!input_str.empty()) download_origin_url = input_str; + } + + std::string download_file_url = url_decode(download_origin_url); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << download_file_url; + if (!download_file_url.empty() && ( boost::starts_with(download_file_url, "http://") || boost::starts_with(download_file_url, "https://")) ) { + request_model_download(download_origin_url); + } } else { - mainframe->select_tab(size_t(MainFrame::tp3DEditor)); - plater_->select_view_3D("3D"); - const std::vector res = this->plater()->load_files(this->init_params->input_files); - if (!res.empty()) { - if (this->init_params->input_files.size() == 1) { - // Update application titlebar when opening a project file - const std::string& filename = this->init_params->input_files.front(); - //BBS: remove amf logic as project - if (boost::algorithm::iends_with(filename, ".3mf")) - this->plater()->set_project_filename(from_u8(filename)); + switch_to_3d = true; + if (this->init_params->input_gcode) { + mainframe->select_tab(size_t(MainFrame::tp3DEditor)); + plater_->select_view_3D("3D"); + this->plater()->load_gcode(from_u8(this->init_params->input_files.front())); + } + else { + mainframe->select_tab(size_t(MainFrame::tp3DEditor)); + plater_->select_view_3D("3D"); + Plater::TakeSnapshot snapshot(this->plater(), "Load Project", UndoRedo::SnapshotType::ProjectSeparator); + const std::vector res = this->plater()->load_files(this->init_params->input_files); + if (!res.empty()) { + if (this->init_params->input_files.size() == 1) { + // Update application titlebar when opening a project file + const std::string& filename = this->init_params->input_files.front(); + this->plater()->up_to_date(true, false); + this->plater()->up_to_date(true, true); + //BBS: remove amf logic as project + if (boost::algorithm::iends_with(filename, ".3mf")) + this->plater()->set_project_filename(from_u8(filename)); + } } } } } + //#if BBL_HAS_FIRST_PAGE bool slow_bootup = false; if (app_config->get("slow_bootup") == "true") { @@ -1017,9 +1061,9 @@ void GUI_App::post_init() mainframe->select_tab(size_t(MainFrame::tp3DEditor)); plater_->select_view_3D("3D"); //BBS init the opengl resource here -#ifdef __linux__ +//#ifdef __linux__ if (plater_->canvas3D()->get_wxglcanvas()->IsShownOnScreen()&&plater_->canvas3D()->make_current_for_postinit()) { -#endif +//#endif Size canvas_size = plater_->canvas3D()->get_canvas_size(); wxGetApp().imgui()->set_display_size(static_cast(canvas_size.get_width()), static_cast(canvas_size.get_height())); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", start to init opengl"; @@ -1039,9 +1083,12 @@ void GUI_App::post_init() plater_->canvas3D()->render(false); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", finished rendering a first frame for test"; } -#ifdef __linux__ +//#ifdef __linux__ } -#endif + else { + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << "Found glcontext not ready, postpone the init"; + } +//#endif if (is_editor()) mainframe->select_tab(size_t(0)); mainframe->Thaw(); @@ -1123,7 +1170,8 @@ void GUI_App::post_init() std::string http_url = get_http_url(app_config->get_country_code()); std::string language = GUI::into_u8(current_language_code()); std::string network_ver = Slic3r::NetworkAgent::get_version(); - this->preset_updater->sync(http_url, language, network_ver, preset_bundle); + bool sys_preset = app_config->get("sync_system_preset") == "true"; + this->preset_updater->sync(http_url, language, network_ver, sys_preset ? preset_bundle : nullptr); //BBS: check new version this->check_new_version_sf(); @@ -1296,12 +1344,40 @@ std::string GUI_App::get_http_url(std::string country_code) return url; } +std::string GUI_App::get_model_http_url(std::string country_code) +{ + std::string url; + if (country_code == "US") { + url = "https://makerhub.bambu-lab.com/"; + } + else if (country_code == "CN") { + url = "https://makerhub.bambu-lab.com/zh/"; + } + else if (country_code == "ENV_CN_DEV") { + url = "https://makerhub-dev.bambu-lab.com/"; + } + else if (country_code == "ENV_CN_QA") { + url = "https://makerhub-qa.bambu-lab.com/"; + } + else if (country_code == "ENV_CN_PRE") { + url = "https://makerhub-pre.bambu-lab.com/"; + } + else { + url = "https://makerhub.bambu-lab.com/"; + } + + return url; +} + + std::string GUI_App::get_plugin_url(std::string name, std::string country_code) { std::string url = get_http_url(country_code); std::string curr_version = SLIC3R_VERSION; std::string using_version = curr_version.substr(0, 9) + "00"; + if (name == "cameratools") + using_version = curr_version.substr(0, 6) + "00.00"; url += (boost::format("?slicer/%1%/cloud=%2%") % name % using_version).str(); //url += (boost::format("?slicer/plugins/cloud=%1%") % "01.01.00.00").str(); return url; @@ -1325,10 +1401,19 @@ static std::string decode(std::string const& extra, std::string const& path = {} int GUI_App::download_plugin(std::string name, std::string package_name, InstallProgressFn pro_fn, WasCancelledFn cancel_fn) { int result = 0; + json j; + std::string err_msg; + // get country_code AppConfig* app_config = wxGetApp().app_config; - if (!app_config) + if (!app_config) { + j["result"] = "failed"; + j["error_msg"] = "app_config is nullptr"; + if (m_agent) { + m_agent->track_event("networkplugin_download", j.dump()); + } return -1; + } BOOST_LOG_TRIVIAL(info) << "[download_plugin]: enter"; m_networking_cancel_update = false; @@ -1388,21 +1473,32 @@ int GUI_App::download_plugin(std::string name, std::string package_name, Install ; } }).on_error( - [&result](std::string body, std::string error, unsigned int status) { + [&result, &err_msg](std::string body, std::string error, unsigned int status) { BOOST_LOG_TRIVIAL(error) << "[download_plugin 1] on_error: " << error<<", body = " << body; + err_msg += "[download_plugin 1] on_error: " + error + ", body = " + body; result = -1; }).perform_sync(); bool cancel = false; if (result < 0) { + j["result"] = "failed"; + j["error_msg"] = err_msg; + if (m_agent) { + m_agent->track_event("networkplugin_download", j.dump()); + } if (pro_fn) pro_fn(InstallStatusDownloadFailed, 0, cancel); return result; } if (download_url.empty()) { - BOOST_LOG_TRIVIAL(info) << "[download_plugin 1]: no availaible plugin found for this app version: " << SLIC3R_VERSION; + BOOST_LOG_TRIVIAL(info) << "[download_plugin 1]: no available plugin found for this app version: " << SLIC3R_VERSION; if (pro_fn) pro_fn(InstallStatusDownloadFailed, 0, cancel); + j["result"] = "failed"; + j["error_msg"] = "[download_plugin 1]: no available plugin found for this app version: " + std::string(SLIC3R_VERSION); + if (m_agent) { + m_agent->track_event("networkplugin_download", j.dump()); + } return -1; } else if (pro_fn) { @@ -1411,6 +1507,11 @@ int GUI_App::download_plugin(std::string name, std::string package_name, Install if (m_networking_cancel_update || cancel) { BOOST_LOG_TRIVIAL(info) << boost::format("[download_plugin 1]: %1%, cancelled by user") % __LINE__; + j["result"] = "failed"; + j["error_msg"] = (boost::format("[download_plugin 1]: %1%, cancelled by user") % __LINE__).str(); + if (m_agent) { + m_agent->track_event("networkplugin_download", j.dump()); + } return -1; } BOOST_LOG_TRIVIAL(info) << "[download_plugin] get_url = " << download_url; @@ -1419,7 +1520,7 @@ int GUI_App::download_plugin(std::string name, std::string package_name, Install Slic3r::Http http = Slic3r::Http::get(download_url); int reported_percent = 0; http.on_progress( - [this, &pro_fn, cancel_fn, &result, &reported_percent](Slic3r::Http::Progress progress, bool& cancel) { + [this, &pro_fn, cancel_fn, &result, &reported_percent, &err_msg](Slic3r::Http::Progress progress, bool& cancel) { int percent = 0; if (progress.dltotal != 0) percent = progress.dlnow * 50 / progress.dltotal; @@ -1434,8 +1535,10 @@ int GUI_App::download_plugin(std::string name, std::string package_name, Install if (cancel_fn()) cancel = true; - if (cancel) + if (cancel) { + err_msg += "[download_plugin] cancel"; result = -1; + } }) .on_complete([&pro_fn, tmp_path, target_file_path](std::string body, unsigned status) { BOOST_LOG_TRIVIAL(info) << "[download_plugin 2] completed"; @@ -1447,13 +1550,19 @@ int GUI_App::download_plugin(std::string name, std::string package_name, Install fs::rename(tmp_path, target_file_path); if (pro_fn) pro_fn(InstallStatusDownloadCompleted, 80, cancel); }) - .on_error([&pro_fn, &result](std::string body, std::string error, unsigned int status) { + .on_error([&pro_fn, &result, &err_msg](std::string body, std::string error, unsigned int status) { bool cancel = false; if (pro_fn) pro_fn(InstallStatusDownloadFailed, 0, cancel); BOOST_LOG_TRIVIAL(error) << "[download_plugin 2] on_error: " << error<<", body = " << body; + err_msg += "[download_plugin 2] on_error: " + error + ", body = " + body; result = -1; }); http.perform_sync(); + j["result"] = result < 0 ? "failed" : "success"; + j["error_msg"] = err_msg; + if (m_agent) { + m_agent->track_event("networkplugin_download", j.dump()); + } return result; } @@ -1605,6 +1714,8 @@ void GUI_App::restart_networking() mainframe->refresh_plugin_tips(); if (plater_) plater_->get_notification_manager()->bbl_close_plugin_install_notification(); + + if (app_config->get("sync_user_preset") == "true") { start_sync_user_preset(); } } BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(" exit, m_agent=%1%")%m_agent; } @@ -1730,7 +1841,7 @@ void GUI_App::init_networking_callbacks() } else if (state == ConnectStatus::ConnectStatusFailed) { obj->set_access_code(""); obj->set_user_access_code(""); - m_device_manager->set_selected_machine(""); + m_device_manager->set_selected_machine("", true); wxString text; if (msg == "5") { text = wxString::Format(_L("Incorrect password")); @@ -1741,7 +1852,7 @@ void GUI_App::init_networking_callbacks() } event.SetInt(0); } else if (state == ConnectStatus::ConnectStatusLost) { - m_device_manager->set_selected_machine(""); + m_device_manager->set_selected_machine("", true); event.SetInt(0); BOOST_LOG_TRIVIAL(info) << "set_on_local_connect_fn: state = lost"; } else { @@ -1932,6 +2043,19 @@ void GUI_App::init_app_config() m_datadir_redefined = true; } + // start log here + std::time_t t = std::time(0); + std::tm * now_time = std::localtime(&t); + std::stringstream buf; + buf << std::put_time(now_time, "debug_%a_%b_%d_%H_%M_%S_"); + buf << get_current_pid() << ".log"; + std::string log_filename = buf.str(); +#if !BBL_RELEASE_TO_PUBLIC + set_log_path_and_level(log_filename, 5); +#else + set_log_path_and_level(log_filename, 3); +#endif + //BBS: remove GCodeViewer as seperate APP logic if (!app_config) app_config = new AppConfig(); @@ -1983,6 +2107,7 @@ std::map GUI_App::get_extra_header() { std::map extra_headers; extra_headers.insert(std::make_pair("X-BBL-Client-Type", "slicer")); + extra_headers.insert(std::make_pair("X-BBL-Client-Name", SLIC3R_APP_NAME)); extra_headers.insert(std::make_pair("X-BBL-Client-Version", VersionInfo::convert_full_version(SLIC3R_VERSION))); #if defined(__WINDOWS__) extra_headers.insert(std::make_pair("X-BBL-OS-Type", "windows")); @@ -2055,19 +2180,6 @@ bool GUI_App::OnInit() bool GUI_App::on_init_inner() { - //start log here - std::time_t t = std::time(0); - std::tm* now_time = std::localtime(&t); - std::stringstream buf; - buf << std::put_time(now_time, "debug_%a_%b_%d_%H_%M_%S_"); - buf << get_current_pid() << ".log"; - std::string log_filename = buf.str(); -#if !BBL_RELEASE_TO_PUBLIC - set_log_path_and_level(log_filename, 5); -#else - set_log_path_and_level(log_filename, 3); -#endif - // Set initialization of image handlers before any UI actions - See GH issue #7469 wxInitAllImageHandlers(); #ifdef NDEBUG @@ -2426,7 +2538,7 @@ bool GUI_App::on_init_inner() sidebar().obj_list()->init(); //sidebar().aux_list()->init_auxiliary(); - mainframe->m_auxiliary->init_auxiliary(); + //mainframe->m_auxiliary->init_auxiliary(); // update_mode(); // !!! do that later SetTopWindow(mainframe); @@ -2493,12 +2605,18 @@ bool GUI_App::on_init_inner() if (m_studio_active != curr_studio_active) { if (curr_studio_active) { BOOST_LOG_TRIVIAL(info) << "studio is active, start to subscribe"; - if (m_agent) + if (m_agent) { + json j = json::object(); m_agent->start_subscribe("app"); + m_agent->track_event("mqtt_active", j.dump()); + } } else { BOOST_LOG_TRIVIAL(info) << "studio is inactive, stop to subscribe"; - if (m_agent) + if (m_agent) { + json j = json::object(); m_agent->stop_subscribe("app"); + m_agent->track_event("mqtt_inactive", j.dump()); + } } m_studio_active = curr_studio_active; } @@ -2528,6 +2646,11 @@ bool GUI_App::on_init_inner() this->mainframe->register_win32_callbacks(); #endif this->post_init(); + + if (!m_download_file_url.empty()) { + request_model_download(m_download_file_url); + m_download_file_url = ""; + } } }); @@ -2741,8 +2864,9 @@ void GUI_App::init_label_colours() m_color_label_default = is_dark_mode ? wxColour(250, 250, 250) : m_color_label_sys; // wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); m_color_highlight_label_default = is_dark_mode ? wxColour(230, 230, 230): wxSystemSettings::GetColour(/*wxSYS_COLOUR_HIGHLIGHTTEXT*/wxSYS_COLOUR_WINDOWTEXT); m_color_highlight_default = is_dark_mode ? wxColour(78, 78, 78) : wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT); - m_color_hovered_btn_label = is_dark_mode ? wxColour(253, 111, 40) : wxColour(252, 77, 1); - m_color_selected_btn_bg = is_dark_mode ? wxColour(95, 73, 62) : wxColour(228, 220, 216); + m_color_hovered_btn_label = is_dark_mode ? wxColour(255, 255, 254) : wxColour(0,0,0); + m_color_default_btn_label = is_dark_mode ? wxColour(255, 255, 254): wxColour(0,0,0); + m_color_selected_btn_bg = is_dark_mode ? wxColour(84, 84, 91) : wxColour(206, 206, 206); #else m_color_label_default = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); #endif @@ -2771,11 +2895,59 @@ void GUI_App::update_label_colours() tab->update_label_colours(); } +#ifdef _WIN32 +static bool is_focused(HWND hWnd) +{ + HWND hFocusedWnd = ::GetFocus(); + return hFocusedWnd && hWnd == hFocusedWnd; +} + +static bool is_default(wxWindow* win) +{ + wxTopLevelWindow* tlw = find_toplevel_parent(win); + if (!tlw) + return false; + + return win == tlw->GetDefaultItem(); +} +#endif + void GUI_App::UpdateDarkUI(wxWindow* window, bool highlited/* = false*/, bool just_font/* = false*/) { - if (wxButton *btn = dynamic_cast(window)) { + if (wxButton *btn = dynamic_cast(window)) { if (btn->GetWindowStyleFlag() & wxBU_AUTODRAW) return; + else { +#ifdef _WIN32 + if (btn->GetId() == wxID_OK || btn->GetId() == wxID_CANCEL) { + bool is_focused_button = false; + bool is_default_button = false; + + if (!(btn->GetWindowStyle() & wxNO_BORDER)) { + btn->SetWindowStyle(btn->GetWindowStyle() | wxNO_BORDER); + highlited = true; + } + + auto mark_button = [this, btn, highlited](const bool mark) { + btn->SetBackgroundColour(mark ? m_color_selected_btn_bg : highlited ? m_color_highlight_default : m_color_window_default); + btn->SetForegroundColour(mark ? m_color_hovered_btn_label :m_color_default_btn_label); + btn->Refresh(); + btn->Update(); + }; + + // hovering + btn->Bind(wxEVT_ENTER_WINDOW, [mark_button](wxMouseEvent& event) { mark_button(true); event.Skip(); }); + btn->Bind(wxEVT_LEAVE_WINDOW, [mark_button, btn](wxMouseEvent& event) { mark_button(is_focused(btn->GetHWND())); event.Skip(); }); + // focusing + btn->Bind(wxEVT_SET_FOCUS, [mark_button](wxFocusEvent& event) { mark_button(true); event.Skip(); }); + btn->Bind(wxEVT_KILL_FOCUS, [mark_button](wxFocusEvent& event) { mark_button(false); event.Skip(); }); + + is_focused_button = is_focused(btn->GetHWND()); + is_default_button = is_default(btn); + mark_button(is_focused_button); + } +#endif + } } if (Button* btn = dynamic_cast(window)) { @@ -3055,7 +3227,7 @@ void GUI_App::recreate_GUI(const wxString& msg_name) // Propagate model objects to object list. sidebar().obj_list()->init(); //sidebar().aux_list()->init_auxiliary(); - mainframe->m_auxiliary->init_auxiliary(); + //mainframe->m_auxiliary->init_auxiliary(); SetTopWindow(mainframe); dlg.Update(30, _L("Rebuild") + dots); @@ -3085,6 +3257,21 @@ void GUI_App::recreate_GUI(const wxString& msg_name) // config_wizard_startup(true); // }); + //show publish button + if (m_agent && m_agent->is_user_login() && mainframe) { + int identifier; + int result = m_agent->get_user_info(&identifier); + auto publish_identifier = identifier & 1; + +#ifdef __WINDOWS__ + if (result == 0 && publish_identifier >= 0) { + mainframe->m_topbar->show_publish_button(publish_identifier == 0 ? false : true); + } +#else + mainframe->show_publish_button(publish_identifier == 0 ? false : true); +#endif + } + m_is_recreating_gui = false; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "recreate_GUI exit"; @@ -3436,22 +3623,6 @@ std::string GUI_App::handle_web_request(std::string cmd) { try { //BBS use nlohmann json format - json j = json::parse(cmd); - - std::string web_cmd = j["command"].get(); - - if (web_cmd == "request_model_download") { - std::string download_url = ""; - if (j["data"].contains("download_url")) - download_url = j["data"]["download_url"].get(); - - std::string filename = ""; - if (j["data"].contains("filename")) - download_url = j["data"]["filename"].get(); - - this->request_model_download(download_url, filename); - } - std::stringstream ss(cmd), oss; pt::ptree root, response; pt::read_json(ss, root); @@ -3631,12 +3802,10 @@ void GUI_App::handle_script_message(std::string msg) } } -void GUI_App::request_model_download(std::string url, std::string filename) +void GUI_App::request_model_download(std::string url) { - if (!check_login()) return; - if (plater_) { - plater_->request_model_download(); + plater_->request_model_download(url); } } @@ -3771,15 +3940,15 @@ void GUI_App::on_user_login_handle(wxCommandEvent &evt) wxQueueEvent(this, evt); }); - // if (app_config->get("sync_user_preset") == "true") { - enable_user_preset_folder(true); - // } else { - // enable_user_preset_folder(false); - // } + if (online_login) GUI::wxGetApp().mainframe->show_sync_dialog(); - + else if (app_config->get("sync_user_preset") == "true") { + enable_user_preset_folder(true); + } else { + enable_user_preset_folder(false); + } //show publish button if (m_agent->is_user_login() && mainframe) { int identifier; @@ -3796,12 +3965,30 @@ void GUI_App::on_user_login_handle(wxCommandEvent &evt) } } +void GUI_App::check_track_enable() +{ + if (app_config && app_config->get("firstguide", "privacyuse") == "true") { + //enable track event + json header_json; + header_json["ver"] = SLIC3R_VERSION; + wxString os_desc = wxGetOsDescription(); + int major = 0, minor = 0, micro = 0; + header_json["os"] = std::string(os_desc.ToUTF8()); + header_json["name"] = std::string(SLIC3R_APP_NAME); + if (m_agent) { + m_agent->track_header(header_json.dump()); + m_agent->track_enable(true); + } + } +} + void GUI_App::on_user_login(wxCommandEvent &evt) { if (!m_agent) { return; } int online_login = evt.GetInt(); // check privacy before handle check_privacy_version(online_login); + check_track_enable(); } bool GUI_App::is_studio_active() @@ -4295,6 +4482,9 @@ void GUI_App::start_sync_user_preset(bool load_immediately, bool with_progress_d { if (!m_agent || !m_agent->is_user_login()) return; + if (load_immediately) + remove_user_presets(); + enable_user_preset_folder(true); // has already start sync @@ -4411,6 +4601,9 @@ void GUI_App::stop_sync_user_preset() remove_user_presets(); enable_user_preset_folder(false); + preset_bundle->load_user_presets(DEFAULT_USER_FOLDER_NAME, ForwardCompatibilitySubstitutionRule::Enable); + mainframe->update_side_preset_ui(); + if (!enable_sync) return; @@ -4519,6 +4712,7 @@ int GUI_App::GetSingleChoiceIndex(const wxString& message, { #ifdef _WIN32 wxSingleChoiceDialog dialog(nullptr, message, caption, choices); + dialog.SetBackgroundColour(*wxWHITE); wxGetApp().UpdateDlgDarkUI(&dialog); dialog.SetSelection(initialSelection); @@ -4733,7 +4927,8 @@ bool GUI_App::load_language(wxString language, bool initial) wxLANGUAGE_SWEDISH, wxLANGUAGE_DUTCH, wxLANGUAGE_HUNGARIAN, - wxLANGUAGE_JAPANESE + wxLANGUAGE_JAPANESE, + wxLANGUAGE_ITALIAN }; std::string cur_language = app_config->get("language"); if (cur_language != "") { @@ -4800,6 +4995,11 @@ Tab* GUI_App::get_model_tab(bool part) return model_tabs_list[part ? 1 : 0]; } +Tab* GUI_App::get_layer_tab() +{ + return model_tabs_list[2]; +} + ConfigOptionMode GUI_App::get_mode() { if (!app_config->has("user_mode")) @@ -5142,6 +5342,8 @@ bool GUI_App::check_and_save_current_preset_changes(const wxString& caption, con int act_buttons = UnsavedChangesDialog::ActionButtons::SAVE; if (dont_save_insted_of_discard) act_buttons |= UnsavedChangesDialog::ActionButtons::DONT_SAVE; + if (remember_choice) + act_buttons |= UnsavedChangesDialog::ActionButtons::REMEMBER_CHOISE; UnsavedChangesDialog dlg(caption, header, "", act_buttons); if (dlg.ShowModal() == wxID_CANCEL) return false; @@ -5390,6 +5592,33 @@ void GUI_App::OSXStoreOpenFiles(const wxArrayString &fileNames) }*/ wxApp::OSXStoreOpenFiles(fileNames); } + +void GUI_App::MacOpenURL(const wxString& url) +{ + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "get mac url " << url; + + if (!url.empty() && boost::starts_with(url, "bambustudioopen://")) { + auto input_str_arr = split_str(url.ToStdString(), "bambustudioopen://"); + + std::string download_origin_url; + for (auto input_str : input_str_arr) { + if (!input_str.empty()) download_origin_url = input_str; + } + + std::string download_file_url = url_decode(download_origin_url); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << download_file_url; + if (!download_file_url.empty() && (boost::starts_with(download_file_url, "http://") || boost::starts_with(download_file_url, "https://"))) { + + if (m_post_initialized) { + request_model_download(download_file_url); + } + else { + m_download_file_url = download_file_url; + } + } + } +} + // wxWidgets override to get an event on open files. void GUI_App::MacOpenFiles(const wxArrayString &fileNames) { @@ -5455,6 +5684,7 @@ void GUI_App::MacOpenFiles(const wxArrayString &fileNames) start_new_gcodeviewer(&filename);*/ } } + #endif /* __APPLE */ Sidebar& GUI_App::sidebar() @@ -5472,6 +5702,11 @@ ObjectList* GUI_App::obj_list() return sidebar().obj_list(); } +ObjectLayers* GUI_App::obj_layers() +{ + return sidebar().obj_layers(); +} + Plater* GUI_App::plater() { return plater_; @@ -5509,45 +5744,85 @@ void GUI_App::load_url(wxString url) void GUI_App::open_mall_page_dialog() { - std::string url; + std::string host_url; + std::string model_url; + std::string link_url; + + int result = -1; + + //model api url + host_url = get_model_http_url(app_config->get_country_code()); + + //model url + + wxString language_code = this->current_language_code().BeforeFirst('_'); + model_url = language_code.ToStdString(); if (getAgent() && mainframe) { - getAgent()->get_model_mall_home_url(&url); - if (!m_mall_home_dialog) { - m_mall_home_dialog = new ModelMallDialog(); - m_mall_home_dialog->go_to_mall(url); - } - else { - if (m_mall_home_dialog->IsIconized()) - m_mall_home_dialog->Iconize(false); + //login already + if (getAgent()->is_user_login()) { + std::string ticket; + result = getAgent()->request_bind_ticket(&ticket); - //m_mall_home_dialog->go_to_mall(url); + if(result == 0){ + link_url = host_url + "api/sign-in/ticket?to=" + host_url + url_encode(model_url) + "&ticket=" + ticket; + } } - m_mall_home_dialog->Raise(); - m_mall_home_dialog->Show(); } + + if (result < 0) { + link_url = host_url + model_url; + } + + wxLaunchDefaultBrowser(link_url); } void GUI_App::open_publish_page_dialog() { - std::string url; + std::string host_url; + std::string model_url; + std::string link_url; + + int result = -1; + + //model api url + host_url = get_model_http_url(app_config->get_country_code()); + + //publish url + wxString language_code = this->current_language_code().BeforeFirst('_'); + model_url += (language_code.ToStdString() + "/my/models/publish"); + if (getAgent() && mainframe) { - getAgent()->get_model_publish_url(&url); - if (!m_mall_publish_dialog) { - m_mall_publish_dialog = new ModelMallDialog(); - m_mall_publish_dialog->go_to_mall(url); - } - else { - if (m_mall_publish_dialog->IsIconized()) - m_mall_publish_dialog->Iconize(false); + //login already + if (getAgent()->is_user_login()) { + std::string ticket; + result = getAgent()->request_bind_ticket(&ticket); - //m_mall_publish_dialog->go_to_publish(url); + if (result == 0) { + link_url = host_url + "api/sign-in/ticket?to=" + host_url + url_encode(model_url) + "&ticket=" + ticket; + } } - m_mall_publish_dialog->Raise(); - m_mall_publish_dialog->Show(); } + + if (result < 0) { + link_url = host_url + model_url; + } + + wxLaunchDefaultBrowser(link_url); +} + +char GUI_App::from_hex(char ch) { + return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10; +} + +std::string GUI_App::url_decode(std::string value) { + return Http::url_decode(value); +} + +std::string GUI_App::url_encode(std::string value) { + return Http::url_encode(value); } void GUI_App::remove_mall_system_dialog() @@ -5556,12 +5831,6 @@ void GUI_App::remove_mall_system_dialog() m_mall_publish_dialog->Destroy(); delete m_mall_publish_dialog; } - - - if (m_mall_home_dialog != nullptr) { - m_mall_home_dialog->Destroy(); - delete m_mall_home_dialog; - } } void GUI_App::run_script(wxString js) diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index d370a1fe6a..5a64c735b4 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -66,6 +66,7 @@ class MainFrame; class Sidebar; class ObjectSettings; class ObjectList; +class ObjectLayers; class Plater; class ParamsPanel; class NotificationManager; @@ -227,6 +228,8 @@ private: bool m_opengl_initialized{ false }; #endif +//import model from mall + std::string m_download_file_url; //#ifdef _WIN32 wxColour m_color_label_modified; @@ -408,7 +411,7 @@ private: int request_user_unbind(std::string dev_id); std::string handle_web_request(std::string cmd); void handle_script_message(std::string msg); - void request_model_download(std::string url, std::string filename); + void request_model_download(std::string url); void download_project(std::string project_id); void request_project_download(std::string project_id); void request_open_project(std::string project_id); @@ -449,6 +452,7 @@ private: void on_check_privacy_update(wxCommandEvent &evt); bool check_privacy_update(); void check_privacy_version(int online_login = 0); + void check_track_enable(); static bool catch_error(std::function cb, const std::string& err); @@ -460,6 +464,7 @@ private: Tab* get_tab(Preset::Type type); Tab* get_model_tab(bool part = false); + Tab* get_layer_tab(); ConfigOptionMode get_mode(); void save_mode(const /*ConfigOptionMode*/int mode) ; void update_mode(); @@ -501,11 +506,13 @@ private: void OSXStoreOpenFiles(const wxArrayString &files); // wxWidgets override to get an event on open files. void MacOpenFiles(const wxArrayString &fileNames) override; + void MacOpenURL(const wxString& url) override; #endif /* __APPLE */ Sidebar& sidebar(); ObjectSettings* obj_settings(); ObjectList* obj_list(); + ObjectLayers* obj_layers(); Plater* plater(); const Plater* plater() const; ParamsPanel* params_panel(); @@ -516,7 +523,6 @@ private: std::string m_mall_model_download_url; std::string m_mall_model_download_name; - ModelMallDialog* m_mall_home_dialog{ nullptr }; ModelMallDialog* m_mall_publish_dialog{ nullptr }; void set_download_model_url(std::string url) {m_mall_model_download_url = url;} @@ -532,6 +538,10 @@ private: bool is_adding_script_handler() { return m_adding_script_handler; } void set_adding_script_handler(bool status) { m_adding_script_handler = status; } + char from_hex(char ch); + std::string url_encode(std::string value); + std::string url_decode(std::string value); + // Parameters extracted from the command line to be passed to GUI after initialization. GUI_InitParams* init_params { nullptr }; @@ -593,6 +603,7 @@ private: int download_plugin(std::string name, std::string package_name, InstallProgressFn pro_fn = nullptr, WasCancelledFn cancel_fn = nullptr); int install_plugin(std::string name, std::string package_name, InstallProgressFn pro_fn = nullptr, WasCancelledFn cancel_fn = nullptr); std::string get_http_url(std::string country_code); + std::string get_model_http_url(std::string country_code); bool is_compatibility_version(); bool check_networking_version(); void cancel_networking_install(); diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index e859811c35..8612de690f 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -18,6 +18,7 @@ #include #include "slic3r/Utils/FixModelByWin10.hpp" +#include "ParamsPanel.hpp" namespace Slic3r { @@ -200,7 +201,7 @@ std::map> SettingsFactory::get_all_v } -SettingsFactory::Bundle SettingsFactory::get_bundle(const DynamicPrintConfig* config, bool is_object_settings) +SettingsFactory::Bundle SettingsFactory::get_bundle(const DynamicPrintConfig* config, bool is_object_settings, bool is_layer_settings/* = false*/) { auto opt_keys = config->keys(); if (opt_keys.empty()) @@ -208,6 +209,8 @@ SettingsFactory::Bundle SettingsFactory::get_bundle(const DynamicPrintConfig* co // update options list according to print technology auto full_current_opts = get_options(!is_object_settings); + if (is_layer_settings) + full_current_opts.push_back("layer_height"); for (int i = opt_keys.size() - 1; i >= 0; --i) if (find(full_current_opts.begin(), full_current_opts.end(), opt_keys[i]) == full_current_opts.end()) opt_keys.erase(opt_keys.begin() + i); @@ -448,7 +451,7 @@ void MenuFactory::append_menu_item_set_visible(wxMenu* menu) void MenuFactory::append_menu_item_delete(wxMenu* menu) { #ifdef __WINDOWS__ - append_menu_item(menu, wxID_ANY, _L("Delete") + "\tDel", _L("Delete the selected object"), + append_menu_item(menu, wxID_ANY, _L("Delete") + "\t" + _L("Del"), _L("Delete the selected object"), [](wxCommandEvent&) { plater()->remove_selected(); }, "menu_delete", nullptr, []() { return plater()->can_delete(); }, m_parent); #else @@ -534,6 +537,15 @@ void MenuFactory::append_menu_items_add_volume(wxMenu* menu) append_submenu(menu, sub_menu, wxID_ANY, _(item.first), "", item.second, []() { return obj_list()->is_instance_or_object_selected(); }, m_parent); } + + append_menu_item_layers_editing(menu); +} + +wxMenuItem* MenuFactory::append_menu_item_layers_editing(wxMenu* menu) +{ + return append_menu_item(menu, wxID_ANY, _L("Height range Modifier"), "", + [](wxCommandEvent&) { obj_list()->layers_editing(); wxGetApp().params_panel()->switch_to_object(); }, "", menu, + []() { return obj_list()->is_instance_or_object_selected(); }, m_parent); } wxMenuItem* MenuFactory::append_menu_item_settings(wxMenu* menu_) @@ -577,7 +589,7 @@ wxMenuItem* MenuFactory::append_menu_item_settings(wxMenu* menu_) if (printer_technology() == ptFFF || (menu->GetMenuItems().size() > 0 && !menu->GetMenuItems().back()->IsSeparator())) - menu->SetFirstSeparator(); + ;// menu->SetFirstSeparator(); // detect itemm for adding of the setting ObjectList* object_list = obj_list(); @@ -601,7 +613,7 @@ wxMenuItem* MenuFactory::append_menu_item_settings(wxMenu* menu_) // BBS remvoe freq setting popupmenu // create_freq_settings_popupmenu(menu, is_object_settings, item); - menu->SetSecondSeparator(); + //menu->SetSecondSeparator(); // Add full settings list auto menu_item = new wxMenuItem(menu, wxID_ANY, menu_name); @@ -942,6 +954,20 @@ void MenuFactory::append_menu_items_mirror(wxMenu* menu) []() { return plater()->can_mirror(); }, m_parent); } +void MenuFactory::append_menu_item_invalidate_cut_info(wxMenu *menu) +{ + const wxString menu_name = _L("Invalidate cut info"); + + auto menu_item_id = menu->FindItem(menu_name); + if (menu_item_id != wxNOT_FOUND) + // Delete old menu item if selected object isn't cut + menu->Destroy(menu_item_id); + + if (obj_list()->has_selected_cut_object()) + append_menu_item(menu, wxID_ANY, menu_name, "", [](wxCommandEvent &) { obj_list()->invalidate_cut_info_for_selection(); }, + "", menu, []() { return true; }, m_parent); +} + MenuFactory::MenuFactory() { for (int i = 0; i < mtCount; i++) { @@ -1199,11 +1225,11 @@ void MenuFactory::create_plate_menu() // delete current plate #ifdef __WINDOWS__ - append_menu_item(menu, wxID_ANY, _L("Delete") + "\tDel", _L("Remove the selected plate"), + append_menu_item(menu, wxID_ANY, _L("Delete Plate"), _L("Remove the selected plate"), [](wxCommandEvent&) { plater()->delete_plate(); }, "menu_delete", nullptr, []() { return plater()->can_delete_plate(); }, m_parent); #else - append_menu_item(menu, wxID_ANY, _L("Delete") + "\tBackSpace", _L("Remove the selected plate"), + append_menu_item(menu, wxID_ANY, _L("Delete Plate"), _L("Remove the selected plate"), [](wxCommandEvent&) { plater()->delete_plate(); }, "", nullptr, []() { return plater()->can_delete_plate(); }, m_parent); #endif @@ -1263,6 +1289,7 @@ wxMenu* MenuFactory::object_menu() append_menu_item_change_filament(&m_object_menu); append_menu_items_convert_unit(&m_object_menu); append_menu_items_flush_options(&m_object_menu); + append_menu_item_invalidate_cut_info(&m_object_menu); return &m_object_menu; } diff --git a/src/slic3r/GUI/GUI_Factories.hpp b/src/slic3r/GUI/GUI_Factories.hpp index a3c01f961f..3d99a072bd 100644 --- a/src/slic3r/GUI/GUI_Factories.hpp +++ b/src/slic3r/GUI/GUI_Factories.hpp @@ -37,7 +37,7 @@ struct SettingsFactory static std::map> PART_CATEGORY_SETTINGS; static wxBitmap get_category_bitmap(const std::string& category_name, bool menu_bmp = true); - static Bundle get_bundle(const DynamicPrintConfig* config, bool is_object_settings); + static Bundle get_bundle(const DynamicPrintConfig* config, bool is_object_settings, bool is_layer_settings = false); static std::vector get_options(bool is_part); //BBS: add api to get options for catogary static std::vector get_visible_options(const std::string& category, const bool is_part); @@ -115,6 +115,7 @@ private: wxMenu* append_submenu_add_generic(wxMenu* menu, ModelVolumeType type); void append_menu_items_add_volume(wxMenu* menu); + wxMenuItem* append_menu_item_layers_editing(wxMenu* menu); wxMenuItem* append_menu_item_settings(wxMenu* menu); wxMenuItem* append_menu_item_change_type(wxMenu* menu); wxMenuItem* append_menu_item_instance_to_object(wxMenu* menu); @@ -136,6 +137,8 @@ private: void append_menu_item_merge_to_single_object(wxMenu* menu); void append_menu_item_merge_parts_to_single_part(wxMenu *menu); void append_menu_items_mirror(wxMenu *menu); + void append_menu_item_invalidate_cut_info(wxMenu *menu); + //void append_menu_items_instance_manipulation(wxMenu *menu); //void update_menu_items_instance_manipulation(MenuType type); //BBS add bbl menu item diff --git a/src/slic3r/GUI/GUI_ObjectLayers.cpp b/src/slic3r/GUI/GUI_ObjectLayers.cpp index a1e4f09c2c..d683cd43da 100644 --- a/src/slic3r/GUI/GUI_ObjectLayers.cpp +++ b/src/slic3r/GUI/GUI_ObjectLayers.cpp @@ -22,24 +22,15 @@ namespace GUI ObjectLayers::ObjectLayers(wxWindow* parent) : OG_Settings(parent, true) { - m_grid_sizer = new wxFlexGridSizer(3, 5, wxGetApp().em_unit()); // "Min Z", "Max Z", "Layer height" & buttons sizer + m_grid_sizer = new wxFlexGridSizer(3, 0, wxGetApp().em_unit()); // "Min Z", "Max Z", "Layer height" & buttons sizer m_grid_sizer->SetFlexibleDirection(wxHORIZONTAL); - // Legend for object layers - for (const std::string col : { L("From height"), L("To height"), L("Layer height") }) { - auto temp = new wxStaticText(m_parent, wxID_ANY, _(col), wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_MIDDLE); - temp->SetBackgroundStyle(wxBG_STYLE_PAINT); - temp->SetFont(wxGetApp().bold_font()); - - m_grid_sizer->Add(temp); - } - m_og->activate(); m_og->sizer->Clear(true); - m_og->sizer->Add(m_grid_sizer, 0, wxEXPAND | wxALL, wxOSX ? 0 : 5); + m_og->sizer->Add(m_grid_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, wxOSX ? 0 : 5); - m_bmp_delete = ScalableBitmap(parent, "remove_copies"/*"cross"*/); - m_bmp_add = ScalableBitmap(parent, "add_copies"); + m_bmp_delete = ScalableBitmap(parent, "delete_filament"/*"cross"*/); + m_bmp_add = ScalableBitmap(parent, "add_filament"); } void ObjectLayers::select_editor(LayerRangeEditor* editor, const bool is_last_edited_range) @@ -80,6 +71,12 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinus m_selection_type = type; }; + // Add text + auto head_text = new wxStaticText(m_parent, wxID_ANY, _L("Height Range"), wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END); + head_text->SetBackgroundStyle(wxBG_STYLE_PAINT); + head_text->SetFont(wxGetApp().normal_font()); + m_grid_sizer->Add(head_text, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, wxGetApp().em_unit()); + // Add control for the "Min Z" auto editor = new LayerRangeEditor(this, double_to_string(range.first), etMinZ, set_focus_data, @@ -103,7 +100,15 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinus }); select_editor(editor, is_last_edited_range); - m_grid_sizer->Add(editor); + + auto sizer1 = new wxBoxSizer(wxHORIZONTAL); + sizer1->Add(editor); + auto middle_text = new wxStaticText(m_parent, wxID_ANY, _L("to"), wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END); + middle_text->SetBackgroundStyle(wxBG_STYLE_PAINT); + middle_text->SetFont(wxGetApp().normal_font()); + sizer1->Add(middle_text, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, wxGetApp().em_unit()); + + m_grid_sizer->Add(sizer1); // Add control for the "Max Z" @@ -127,29 +132,38 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinus }); select_editor(editor, is_last_edited_range); - m_grid_sizer->Add(editor); + auto sizer2 = new wxBoxSizer(wxHORIZONTAL); + sizer2->Add(editor); + auto unit_text = new wxStaticText(m_parent, wxID_ANY, _L("mm"), wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END); + unit_text->SetBackgroundStyle(wxBG_STYLE_PAINT); + unit_text->SetFont(wxGetApp().normal_font()); + sizer2->Add(unit_text, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, wxGetApp().em_unit()); + + m_grid_sizer->Add(sizer2); + + // BBS // Add control for the "Layer height" - editor = new LayerRangeEditor(this, double_to_string(m_object->layer_config_ranges[range].option("layer_height")->getFloat()), etLayerHeight, set_focus_data, - [range](coordf_t layer_height, bool, bool) - { - return wxGetApp().obj_list()->edit_layer_range(range, layer_height); - }); + //editor = new LayerRangeEditor(this, double_to_string(m_object->layer_config_ranges[range].option("layer_height")->getFloat()), etLayerHeight, set_focus_data, + // [range](coordf_t layer_height, bool, bool) + //{ + // return wxGetApp().obj_list()->edit_layer_range(range, layer_height); + //}); - select_editor(editor, is_last_edited_range); + //select_editor(editor, is_last_edited_range); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(editor); + //auto sizer = new wxBoxSizer(wxHORIZONTAL); + //sizer->Add(editor); - auto temp = new wxStaticText(m_parent, wxID_ANY, _L("mm")); - temp->SetBackgroundStyle(wxBG_STYLE_PAINT); - temp->SetFont(wxGetApp().normal_font()); - sizer->Add(temp, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, wxGetApp().em_unit()); + //auto temp = new wxStaticText(m_parent, wxID_ANY, _L("mm")); + //temp->SetBackgroundStyle(wxBG_STYLE_PAINT); + //temp->SetFont(wxGetApp().normal_font()); + //sizer->Add(temp, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, wxGetApp().em_unit()); - m_grid_sizer->Add(sizer); + //m_grid_sizer->Add(sizer); - return sizer; + return sizer2; } void ObjectLayers::create_layers_list() @@ -157,9 +171,11 @@ void ObjectLayers::create_layers_list() for (const auto &layer : m_object->layer_config_ranges) { const t_layer_height_range& range = layer.first; auto del_btn = new PlusMinusButton(m_parent, m_bmp_delete, range); + del_btn->SetBackgroundColour(m_parent->GetBackgroundColour()); del_btn->SetToolTip(_L("Remove height range")); auto add_btn = new PlusMinusButton(m_parent, m_bmp_add, range); + add_btn->SetBackgroundColour(m_parent->GetBackgroundColour()); wxString tooltip = wxGetApp().obj_list()->can_add_new_range_after_current(range); add_btn->SetToolTip(tooltip.IsEmpty() ? _L("Add height range") : tooltip); add_btn->Enable(tooltip.IsEmpty()); @@ -195,18 +211,8 @@ void ObjectLayers::update_layers_list() m_object = objects_ctrl->object(obj_idx); if (!m_object || m_object->layer_config_ranges.empty()) return; - // Delete all controls from options group except of the legends - - const int cols = m_grid_sizer->GetEffectiveColsCount(); - const int rows = m_grid_sizer->GetEffectiveRowsCount(); - for (int idx = cols*rows-1; idx >= cols; idx--) { - wxSizerItem* t = m_grid_sizer->GetItem(idx); - if (t->IsSizer()) - t->GetSizer()->Clear(true); - else - t->DeleteWindows(); - m_grid_sizer->Remove(idx); - } + // Delete all controls from options group + m_grid_sizer->Clear(true); // Add new control according to the selected item @@ -283,7 +289,7 @@ void ObjectLayers::sys_color_changed() if (item->IsSizer()) {// case when we have editor with buttons for (size_t btn : {2, 3}) { // del_btn, add_btn wxSizerItem* b_item = item->GetSizer()->GetItem(btn); - if (b_item->IsWindow()) { + if (b_item && b_item->IsWindow()) { auto button = dynamic_cast(b_item->GetWindow()); if (button != nullptr) button->msw_rescale(); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 08d3b6c4bb..f2c236c3eb 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -34,7 +34,9 @@ #ifdef __WXMSW__ #include "wx/uiaction.h" +#include #endif /* __WXMSW__ */ +#include "Gizmos/GLGizmoScale.hpp" namespace Slic3r { @@ -73,6 +75,17 @@ static void take_snapshot(const std::string& snapshot_name) plater->take_snapshot(snapshot_name); } +class wxRenderer : public wxDelegateRendererNative +{ +public: + wxRenderer() : wxDelegateRendererNative(wxRendererNative::Get()) {} + virtual void DrawItemSelectionRect(wxWindow *win, + wxDC& dc, + const wxRect& rect, + int flags = 0) wxOVERRIDE + { GetGeneric().DrawItemSelectionRect(win, dc, rect, flags); } +}; + ObjectList::ObjectList(wxWindow* parent) : wxDataViewCtrl(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxDV_MULTIPLE) { @@ -80,6 +93,8 @@ ObjectList::ObjectList(wxWindow* parent) : SetFont(Label::sysFont(13)); #ifdef __WXMSW__ GenericGetHeader()->SetFont(Label::sysFont(13)); + static auto render = new wxRenderer; + wxRendererNative::Set(render); #endif // create control @@ -155,6 +170,7 @@ ObjectList::ObjectList(wxWindow* parent) : ObjectDataViewModelNode* sel_node = (ObjectDataViewModelNode*)event.GetItem().GetID(); if (sel_node && (sel_node->GetType() & ItemType::itPlate)) { wxGetApp().plater()->select_plate(sel_node->GetPlateIdx()); + wxGetApp().plater()->deselect_all(); } else { selection_changed(); @@ -264,7 +280,8 @@ ObjectList::ObjectList(wxWindow* parent) : this->CallAfter([this]() { ensure_current_item_visible(); }); #endif e.Skip(); - })); + })); + } ObjectList::~ObjectList() @@ -326,10 +343,11 @@ void ObjectList::create_objects_ctrl() const int em = wxGetApp().em_unit(); m_columns_width.resize(colCount); - m_columns_width[colName] = 25; + m_columns_width[colName] = 22; m_columns_width[colPrint] = 3; m_columns_width[colFilament] = 5; m_columns_width[colSupportPaint] = 3; + m_columns_width[colSinking] = 3; m_columns_width[colColorPaint] = 3; m_columns_width[colEditing] = 3; @@ -369,6 +387,8 @@ void ObjectList::create_objects_ctrl() wxALIGN_CENTER_HORIZONTAL, 0); AppendBitmapColumn(" ", colColorPaint, wxOSX ? wxDATAVIEW_CELL_EDITABLE : wxDATAVIEW_CELL_INERT, m_columns_width[colColorPaint] * em, wxALIGN_CENTER_HORIZONTAL, 0); + AppendBitmapColumn(" ", colSinking, wxOSX ? wxDATAVIEW_CELL_EDITABLE : wxDATAVIEW_CELL_INERT, m_columns_width[colSinking] * em, + wxALIGN_CENTER_HORIZONTAL, 0); // column ItemEditing of the view control: AppendBitmapColumn(" ", colEditing, wxOSX ? wxDATAVIEW_CELL_EDITABLE : wxDATAVIEW_CELL_INERT, m_columns_width[colEditing] * em, @@ -553,6 +573,10 @@ void ObjectList::set_tooltip_for_item(const wxPoint& pt) if (node->HasColorPainting()) tooltip = _(L("Click the icon to edit color painting of the object")); } + else if (col->GetModelColumn() == (unsigned int)colSinking) { + if (node->HasSinking()) + tooltip = _(L("Click the icon to shift this object to the bed")); + } else if (col->GetModelColumn() == (unsigned int)colName && (pt.x >= 2 * wxGetApp().em_unit() && pt.x <= 4 * wxGetApp().em_unit())) { if (const ItemType type = m_objects_model->GetItemType(item); @@ -809,6 +833,12 @@ void ObjectList::set_support_paint_hidden(const bool hide) const update_name_column_width(); } +void GUI::ObjectList::set_sinking_hidden(const bool hide) const +{ + GetColumn(colSinking)->SetHidden(hide); + update_name_column_width(); +} + void ObjectList::update_filament_in_config(const wxDataViewItem& item) { if (m_prevent_update_filament_in_config) @@ -898,6 +928,8 @@ void ObjectList::selection_changed() fix_multiselection_conflicts(); + fix_cut_selection(); + // update object selection on Plater if (!m_prevent_canvas_selection_update) update_selections_on_canvas(); @@ -914,15 +946,13 @@ void ObjectList::selection_changed() const ItemType type = m_objects_model->GetItemType(item); // to correct visual hints for layers editing on the Scene if (type & (itLayer|itLayerRoot)) { - //BBS remove obj_layers - // wxGetApp().obj_layers()->reset_selection(); + wxGetApp().obj_layers()->reset_selection(); if (type & itLayerRoot) wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event("", false); else { - //BBS remove obj_layers - //wxGetApp().obj_layers()->set_selectable_range(m_objects_model->GetLayerRangeByItem(item)); - //wxGetApp().obj_layers()->update_scene_from_editor_selection(); + wxGetApp().obj_layers()->set_selectable_range(m_objects_model->GetLayerRangeByItem(item)); + wxGetApp().obj_layers()->update_scene_from_editor_selection(); } } } @@ -1172,13 +1202,27 @@ void ObjectList::list_manipulation(const wxPoint& mouse_pos, bool evt_context_me gizmos_mgr.reset_all_states(); } } + else if (col_num == colSinking) { + Plater * plater = wxGetApp().plater(); + GLCanvas3D *cnv = plater->canvas3D(); + Plater::TakeSnapshot(plater, "Shift objects to bed"); + int obj_idx, vol_idx; + get_selected_item_indexes(obj_idx, vol_idx, item); + (*m_objects)[obj_idx]->ensure_on_bed(); + cnv->reload_scene(true, true); + update_info_items(obj_idx); + notify_instance_updated(obj_idx); + } else if (col_num == colEditing) { //show_context_menu(evt_context_menu); int obj_idx, vol_idx; get_selected_item_indexes(obj_idx, vol_idx, item); //wxGetApp().plater()->PopupObjectTable(obj_idx, vol_idx, mouse_pos); - dynamic_cast(wxGetApp().get_model_tab(vol_idx >= 0))->reset_model_config(); + if (m_objects_model->GetItemType(item) & itLayer) + dynamic_cast(wxGetApp().get_layer_tab())->reset_model_config(); + else + dynamic_cast(wxGetApp().get_model_tab(vol_idx >= 0))->reset_model_config(); } else if (col_num == colName) { @@ -1215,12 +1259,11 @@ void ObjectList::show_context_menu(const bool evt_context_menu) if (item) { const ItemType type = m_objects_model->GetItemType(item); - if (!(type & (itPlate | itObject | itVolume | itLayer | itInstance))) + if (!(type & (itPlate | itObject | itVolume | itInstance))) return; menu = type & itPlate ? plater->plate_menu() : type & itInstance ? plater->instance_menu() : - type & itLayer ? plater->layer_menu() : type & itVolume ? plater->part_menu() : printer_technology() == ptFFF ? plater->object_menu() : plater->sla_object_menu(); } @@ -2224,9 +2267,9 @@ int ObjectList::load_mesh_part(const TriangleMesh &mesh, const wxString &name, c } //BBS -void ObjectList::del_object(const int obj_idx, bool refresh_immediately) +bool ObjectList::del_object(const int obj_idx, bool refresh_immediately) { - wxGetApp().plater()->delete_object_from_model(obj_idx, refresh_immediately); + return wxGetApp().plater()->delete_object_from_model(obj_idx, refresh_immediately); } // Delete subobject @@ -2243,6 +2286,7 @@ void ObjectList::del_subobject_item(wxDataViewItem& item) wxDataViewItem parent = m_objects_model->GetParent(item); + InfoItemType item_info_type = m_objects_model->GetInfoItemType(item); if (type & itSettings) del_settings_from_config(parent); else if (type & itInstanceRoot && obj_idx != -1) @@ -2252,7 +2296,7 @@ void ObjectList::del_subobject_item(wxDataViewItem& item) else if (type & itLayer && obj_idx != -1) del_layer_from_object(obj_idx, m_objects_model->GetLayerRangeByItem(item)); else if (type & itInfo && obj_idx != -1) - del_info_item(obj_idx, m_objects_model->GetInfoItemType(item)); + del_info_item(obj_idx, item_info_type); else if (idx == -1) return; else if (!del_subobject_from_object(obj_idx, idx, type)) @@ -2264,8 +2308,11 @@ void ObjectList::del_subobject_item(wxDataViewItem& item) m_objects_model->UpdateWarningIcon(parent, icon_name); } - m_objects_model->Delete(item); - update_info_items(obj_idx); + if (!(type & itInfo) || item_info_type != InfoItemType::CutConnectors) { + // Connectors Item is already updated/deleted inside the del_info_item() + m_objects_model->Delete(item); + update_info_items(obj_idx); + } } void ObjectList::del_info_item(const int obj_idx, InfoItemType type) @@ -2289,6 +2336,13 @@ void ObjectList::del_info_item(const int obj_idx, InfoItemType type) mv->mmu_segmentation_facets.reset(); break; + case InfoItemType::CutConnectors: + if (!del_from_cut_object(true)) { + // there is no need to post EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS if nothing was changed + return; + } + break; + // BBS: remove Sinking case InfoItemType::Undef : assert(false); break; } @@ -2359,6 +2413,38 @@ void ObjectList::del_layers_from_object(const int obj_idx) changed_object(obj_idx); } +bool ObjectList::del_from_cut_object(bool is_cut_connector, bool is_model_part/* = false*/, bool is_negative_volume/* = false*/) +{ + const long buttons_style = is_cut_connector ? (wxYES | wxNO | wxCANCEL) : (wxYES | wxCANCEL); + + const wxString title = is_cut_connector ? _L("Delete connector from object which is a part of cut") : + is_model_part ? _L("Delete solid part from object which is a part of cut") : + is_negative_volume ? _L("Delete negative volume from object which is a part of cut") : ""; + + const wxString msg_end = is_cut_connector ? ("\n" + _L("To save cut correspondence you can delete all connectors from all related objects.")) : ""; + + InfoDialog dialog(wxGetApp().plater(), title, + (_L("This action will break a cut correspondence.\n" + "After that model consistency can't be guaranteed .\n" + "\n" + "To manipulate with solid parts or negative volumes you have to invalidate cut infornation first.") + msg_end ), + false, buttons_style | wxCANCEL_DEFAULT | wxICON_WARNING); + + dialog.SetButtonLabel(wxID_YES, _L("Invalidate cut info")); + if (is_cut_connector) + dialog.SetButtonLabel(wxID_NO, _L("Delete all connectors")); + + const int answer = dialog.ShowModal(); + if (answer == wxID_CANCEL) + return false; + + if (answer == wxID_YES) + invalidate_cut_info_for_selection(); + else if (answer == wxID_NO) + delete_all_connectors_for_selection(); + return true; +} + bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, const int type) { assert(idx >= 0); @@ -2383,6 +2469,11 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con Slic3r::GUI::show_error(nullptr, _L("Deleting the last solid part is not allowed.")); return false; } + if (object->is_cut() && (volume->is_model_part() || volume->is_negative_volume())) { + del_from_cut_object(volume->is_cut_connector(), volume->is_model_part(), volume->is_negative_volume()); + // in any case return false to break the deletion + return false; + } take_snapshot("Delete part"); @@ -2774,8 +2865,7 @@ void ObjectList::layers_editing() return; // to correct visual hints for layers editing on the Scene, reset previous selection - //BBS remove obj_layers - //wxGetApp().obj_layers()->reset_selection(); + wxGetApp().obj_layers()->reset_selection(); wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event("", false); // select LayerRoor item and expand @@ -2887,6 +2977,9 @@ bool ObjectList::can_split_instances() bool ObjectList::can_merge_to_multipart_object() const { + if (has_selected_cut_object()) + return false; + if (printer_technology() == ptSLA) return false; @@ -2914,6 +3007,97 @@ bool ObjectList::can_merge_to_single_object() const return (*m_objects)[obj_idx]->volumes.size() > 1; } +bool ObjectList::has_selected_cut_object() const +{ + wxDataViewItemArray sels; + GetSelections(sels); + if (sels.IsEmpty()) + return false; + + for (wxDataViewItem item : sels) { + const int obj_idx = m_objects_model->GetObjectIdByItem(item); + if (obj_idx >= 0 && object(obj_idx)->is_cut()) + return true; + } + + return false; +} + +void ObjectList::invalidate_cut_info_for_selection() +{ + const wxDataViewItem item = GetSelection(); + if (item) { + const int obj_idx = m_objects_model->GetObjectIdByItem(item); + if (obj_idx >= 0) + invalidate_cut_info_for_object(size_t(obj_idx)); + } +} + +void ObjectList::invalidate_cut_info_for_object(int obj_idx) +{ + ModelObject *init_obj = object(obj_idx); + if (!init_obj->is_cut()) return; + + take_snapshot("Invalidate cut info"); + + const CutObjectBase cut_id = init_obj->cut_id; + // invalidate cut for related objects (which have the same cut_id) + for (size_t idx = 0; idx < m_objects->size(); idx++) + if (ModelObject *obj = object(int(idx)); obj->cut_id.is_equal(cut_id)) { + obj->invalidate_cut(); + update_info_items(idx); + add_volumes_to_object_in_list(idx); + } + + update_lock_icons_for_model(); +} + +void ObjectList::delete_all_connectors_for_selection() +{ + const wxDataViewItem item = GetSelection(); + if (item) { + const int obj_idx = m_objects_model->GetObjectIdByItem(item); + if (obj_idx >= 0) + delete_all_connectors_for_object(size_t(obj_idx)); + } +} + +void ObjectList::delete_all_connectors_for_object(int obj_idx) +{ + ModelObject *init_obj = object(obj_idx); + if (!init_obj->is_cut()) + return; + + take_snapshot("Delete all connectors"); + + auto has_solid_mesh = [](ModelObject* obj) { + for (const ModelVolume *volume : obj->volumes) + if (volume->is_model_part()) return true; + return false; + }; + + const CutObjectBase cut_id = init_obj->cut_id; + // Delete all connectors for related objects (which have the same cut_id) + Model &model = wxGetApp().plater()->model(); + for (int idx = int(m_objects->size()) - 1; idx >= 0; idx--) + if (ModelObject *obj = object(idx); obj->cut_id.is_equal(cut_id)) { + obj->delete_connectors(); + + if (obj->volumes.empty() || !has_solid_mesh(obj)) { + model.delete_object(idx); + m_objects_model->Delete(m_objects_model->GetItemById(idx)); + continue; + } + + update_info_items(idx); + add_volumes_to_object_in_list(idx); + changed_object(int(idx)); + } + + update_lock_icons_for_model(); +} + + // NO_PARAMETERS function call means that changed object index will be determine from Selection() void ObjectList::changed_object(const int obj_idx/* = -1*/) const { @@ -2932,15 +3116,77 @@ void ObjectList::part_selection_changed() bool update_and_show_settings = false; bool update_and_show_layers = false; + bool enable_manipulation{true}; + bool disable_ss_manipulation{false}; + bool disable_ununiform_scale{false}; + const auto item = GetSelection(); - // BBS - if (item && (m_objects_model->GetItemType(item) & itPlate)) { + if (item && m_objects_model->GetItemType(item) == itInfo && m_objects_model->GetInfoItemType(item) == InfoItemType::CutConnectors) { + og_name = _L("Cut Connectors information"); + + update_and_show_manipulations = true; + enable_manipulation = false; + disable_ununiform_scale = true; + } + else if (item && (m_objects_model->GetItemType(item) & itPlate)) { + // BBS // TODO: og for plate } else if ( multiple_selection() || (item && m_objects_model->GetItemType(item) == itInstanceRoot )) { const Selection& selection = scene_selection(); - // don't show manipulation panel for case of all Object's parts selection - update_and_show_manipulations = !selection.is_single_full_instance(); + + if (selection.is_single_full_object()) { + og_name = _L("Object manipulation"); + update_and_show_manipulations = true; + + obj_idx = selection.get_object_idx(); + ModelObject *object = (*m_objects)[obj_idx]; + m_config = &object->config; + disable_ss_manipulation = object->is_cut(); + } + else { + og_name = _L("Group manipulation"); + + // don't show manipulation panel for case of all Object's parts selection + update_and_show_manipulations = !selection.is_single_full_instance(); + + if (int obj_idx = selection.get_object_idx(); obj_idx >= 0) { + if (selection.is_any_volume() || selection.is_any_modifier()) + enable_manipulation = !(*m_objects)[obj_idx]->is_cut(); + else // if (item && m_objects_model->GetItemType(item) == itInstanceRoot) + disable_ss_manipulation = (*m_objects)[obj_idx]->is_cut(); + } + else { + wxDataViewItemArray sels; + GetSelections(sels); + if (selection.is_single_full_object() || selection.is_multiple_full_instance()) { + int obj_idx = m_objects_model->GetObjectIdByItem(sels.front()); + disable_ss_manipulation = (*m_objects)[obj_idx]->is_cut(); + } else if (selection.is_mixed() || selection.is_multiple_full_object()) { + std::map> cut_objects; + + // find cut objects + for (auto item : sels) { + int obj_idx = m_objects_model->GetObjectIdByItem(item); + const ModelObject *obj = object(obj_idx); + if (obj->is_cut()) { + if (cut_objects.find(obj->cut_id) == cut_objects.end()) + cut_objects[obj->cut_id] = std::set{obj_idx}; + else + cut_objects.at(obj->cut_id).insert(obj_idx); + } + } + + // check if selected cut objects are "full selected" + for (auto cut_object : cut_objects) + if (cut_object.first.check_sum() != cut_object.second.size()) { + disable_ss_manipulation = true; + break; + } + disable_ununiform_scale = !cut_objects.empty(); + } + } + } // BBS: multi config editing update_and_show_settings = true; @@ -2982,36 +3228,53 @@ void ObjectList::part_selection_changed() // BBS: select object to edit config m_config = &(*m_objects)[obj_idx]->config; update_and_show_settings = true; + disable_ss_manipulation = (*m_objects)[obj_idx]->is_cut(); } } else { if (type & itSettings) { if (parent_type & itObject) { + og_name = _L("Object Settings to modify"); m_config = &(*m_objects)[obj_idx]->config; } else if (parent_type & itVolume) { + og_name = _L("Part Settings to modify"); volume_id = m_objects_model->GetVolumeIdByItem(parent); m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config; } else if (parent_type & itLayer) { + og_name = _L("Layer range Settings to modify"); m_config = &get_item_config(parent); } update_and_show_settings = true; } else if (type & itVolume) { + og_name = _L("Part manipulation"); volume_id = m_objects_model->GetVolumeIdByItem(item); m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config; update_and_show_manipulations = true; m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config; update_and_show_settings = true; + + const ModelVolume *volume = (*m_objects)[obj_idx]->volumes[volume_id]; + enable_manipulation = !((*m_objects)[obj_idx]->is_cut() && (volume->is_cut_connector() || volume->is_model_part())); } else if (type & itInstance) { + og_name = _L("Instance manipulation"); update_and_show_manipulations = true; // fill m_config by object's values m_config = &(*m_objects)[obj_idx]->config; + disable_ss_manipulation = (*m_objects)[obj_idx]->is_cut(); + } + else if (type & (itLayerRoot | itLayer)) { + og_name = type & itLayerRoot ? _L("Height ranges") : _L("Settings for height range"); + update_and_show_layers = true; + update_and_show_settings = true; + + if (type & itLayer) + m_config = &get_item_config(item); } - // BBS: remove height range logics } } } @@ -3028,6 +3291,11 @@ void ObjectList::part_selection_changed() //wxGetApp().obj_manipul()->update_item_name(m_objects_model->GetName(item)); //wxGetApp().obj_manipul()->update_warning_icon_state(get_mesh_errors_info(obj_idx, volume_id)); } + + GLGizmosManager &gizmos_mgr = wxGetApp().plater()->canvas3D()->get_gizmos_manager(); + + if (GLGizmoScale3D *scale = dynamic_cast(gizmos_mgr.get_gizmo(GLGizmosManager::Scale))) + scale->enable_ununiversal_scale(!disable_ununiform_scale); } #if !NEW_OBJECT_SETTING @@ -3038,8 +3306,7 @@ void ObjectList::part_selection_changed() if (printer_technology() == ptSLA) update_and_show_layers = false; else if (update_and_show_layers) { - //BBS remove obj layers - //wxGetApp().obj_layers()->get_og()->set_name(" "); + ;//wxGetApp().obj_layers()->get_og()->set_name(" " + og_name + " "); } update_min_height(); @@ -3051,8 +3318,7 @@ void ObjectList::part_selection_changed() // BBS //wxGetApp().obj_manipul() ->UpdateAndShow(update_and_show_manipulations); wxGetApp().obj_settings()->UpdateAndShow(update_and_show_settings); - //BBS - //wxGetApp().obj_layers() ->UpdateAndShow(update_and_show_layers); + wxGetApp().obj_layers() ->UpdateAndShow(update_and_show_layers); wxGetApp().plater()->show_object_info(); panel.Layout(); @@ -3068,13 +3334,33 @@ wxDataViewItem ObjectList::add_settings_item(wxDataViewItem parent_item, const D return ret; const bool is_object_settings = m_objects_model->GetItemType(parent_item) == itObject; + const bool is_volume_settings = m_objects_model->GetItemType(parent_item) == itVolume; + const bool is_layer_settings = m_objects_model->GetItemType(parent_item) == itLayer; if (!is_object_settings) { ModelVolumeType volume_type = m_objects_model->GetVolumeType(parent_item); if (volume_type == ModelVolumeType::NEGATIVE_VOLUME || volume_type == ModelVolumeType::SUPPORT_BLOCKER || volume_type == ModelVolumeType::SUPPORT_ENFORCER) return ret; } - SettingsFactory::Bundle cat_options = SettingsFactory::get_bundle(config, is_object_settings); + SettingsFactory::Bundle cat_options = SettingsFactory::get_bundle(config, is_object_settings, is_layer_settings); + if (is_layer_settings) { + auto tab_object = dynamic_cast(wxGetApp().get_model_tab()); + auto object_cfg = tab_object->get_config(); + if (config->opt_float("layer_height") == object_cfg->opt_float("layer_height")) { + SettingsFactory::Bundle new_cat_options; + for (auto cat_opt : cat_options) { + std::vector temp; + for (auto value : cat_opt.second) { + if (value != "layer_height") + temp.push_back(value); + } + if (!temp.empty()) + new_cat_options[cat_opt.first] = temp; + } + cat_options = new_cat_options; + } + } + if (cat_options.empty()) { #if NEW_OBJECT_SETTING ObjectDataViewModelNode *node = static_cast(parent_item.GetID()); @@ -3122,6 +3408,33 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selectio wxDataViewItem item_obj = m_objects_model->GetItemById(obj_idx); assert(item_obj.IsOk()); + // Cut connectors + { + wxDataViewItem item = m_objects_model->GetInfoItemByType(item_obj, InfoItemType::CutConnectors); + bool shows = item.IsOk(); + bool should_show = model_object->is_cut() && model_object->has_connectors() && model_object->volumes.size() > 1; + + if (!shows && should_show) { + m_objects_model->AddInfoChild(item_obj, InfoItemType::CutConnectors); + Expand(item_obj); + if (added_object) + wxGetApp().notification_manager()->push_updated_item_info_notification(InfoItemType::CutConnectors); + } else if (shows && !should_show) { + if (!selections) Unselect(item); + m_objects_model->Delete(item); + if (selections) { + if (selections->Index(item) != wxNOT_FOUND) { + // If info item was deleted from the list, + // it's need to be deleted from selection array, if it was there + selections->Remove(item); + // Select item_obj, if info_item doesn't exist for item anymore, but was selected + if (selections->Index(item_obj) == wxNOT_FOUND) selections->Add(item_obj); + } + } else + Select(item_obj); + } + } + { bool shows = m_objects_model->IsSupportPainted(item_obj); bool should_show = printer_technology() == ptFFF @@ -3152,6 +3465,18 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selectio } } + { + bool shows = m_objects_model->IsSinked(item_obj); + bool should_show = printer_technology() == ptFFF + && wxGetApp().plater()->canvas3D()->is_object_sinking(obj_idx); + if (shows && !should_show) { + m_objects_model->SetSinkState(false, item_obj); + } + else if (!shows && should_show) { + m_objects_model->SetSinkState(true, item_obj); + } + } + { bool shows = this->GetColumn(colSupportPaint)->IsShown(); bool should_show = false; @@ -3195,6 +3520,26 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selectio this->set_color_paint_hidden(false); } } + + { + bool shows = this->GetColumn(colSinking)->IsShown(); + bool should_show = false; + for (int i = 0; i < m_objects->size(); ++i) { + if (wxGetApp().plater()->canvas3D()->is_object_sinking(i)) { + should_show = true; + break; + } + if (should_show) + break; + } + + if (shows && !should_show) { + this->set_sinking_hidden(true); + } + else if (!shows && should_show) { + this->set_sinking_hidden(false); + } + } } @@ -3213,24 +3558,12 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed, //const wxString& item_name = from_u8(item_name_str); const wxString& item_name = from_u8(model_object->name); std::string warning_bitmap = get_warning_icon_name(model_object->mesh().stats()); - const auto item = m_objects_model->AddObject(model_object, warning_bitmap); + const auto item = m_objects_model->AddObject(model_object, warning_bitmap, model_object->is_cut()); Expand(m_objects_model->GetParent(item)); update_info_items(obj_idx, nullptr, call_selection_changed); - // add volumes to the object - if (model_object->volumes.size() > 1) { - for (const ModelVolume* volume : model_object->volumes) { - const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(item, - from_u8(volume->name), - volume->type(), - get_warning_icon_name(volume->mesh().stats()), - volume->config.has("extruder") ? volume->config.extruder() : 0, - false); - add_settings_item(vol_item, &volume->config.get()); - } - Expand(item); - } + add_volumes_to_object_in_list(obj_idx); // add instances to the object, if it has those if (model_object->instances.size()>1) @@ -3264,6 +3597,68 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed, #endif //__WXMSW__ } +static bool can_add_volumes_to_object(const ModelObject *object) +{ + bool can = object->volumes.size() > 1; + + if (can && object->is_cut()) { + int no_connectors_cnt = 0; + for (const ModelVolume *v : object->volumes) + if (!v->is_cut_connector()) { + if (!v->is_model_part()) + return true; + no_connectors_cnt++; + } + can = no_connectors_cnt > 1; + } + + return can; +} + +wxDataViewItemArray ObjectList::add_volumes_to_object_in_list(size_t obj_idx, std::function add_to_selection /* = nullptr*/) +{ + const bool is_prevent_list_events = m_prevent_list_events; + m_prevent_list_events = true; + + wxDataViewItem object_item = m_objects_model->GetItemById(int(obj_idx)); + m_objects_model->DeleteVolumeChildren(object_item); + + wxDataViewItemArray items; + + const ModelObject *object = (*m_objects)[obj_idx]; + // add volumes to the object + if (can_add_volumes_to_object(object)) { + if (object->volumes.size() > 1) { + wxString obj_item_name = from_u8(object->name); + if (m_objects_model->GetName(object_item) != obj_item_name) + m_objects_model->SetName(obj_item_name, object_item); + } + + int volume_idx{-1}; + for (const ModelVolume *volume : object->volumes) { + ++volume_idx; + if (object->is_cut() && volume->is_cut_connector()) + continue; + + const wxDataViewItem &vol_item = m_objects_model->AddVolumeChild( + object_item, + from_u8(volume->name), + volume->type(), + get_warning_icon_name(volume->mesh().stats()), + volume->config.has("extruder") ? volume->config.extruder() : 0, + false); + add_settings_item(vol_item, &volume->config.get()); + + if (add_to_selection && add_to_selection(volume)) + items.Add(vol_item); + } + Expand(object_item); + } + + m_prevent_list_events = is_prevent_list_events; + return items; +} + void ObjectList::delete_object_from_list() { auto item = GetSelection(); @@ -3298,8 +3693,12 @@ void ObjectList::delete_from_model_and_list(const ItemType type, const int obj_i take_snapshot("Delete selected"); if (type&itObject) { - del_object(obj_idx); - delete_object_from_list(obj_idx); + bool was_cut = object(obj_idx)->is_cut(); + if (del_object(obj_idx)) { + delete_object_from_list(obj_idx); + if (was_cut) + update_lock_icons_for_model(); + } } else { del_subobject_from_object(obj_idx, sub_obj_idx, type); @@ -3326,12 +3725,16 @@ void ObjectList::delete_from_model_and_list(const std::vector& it // refresh after del_object need_update = true; bool refresh_immediately = false; - del_object(item->obj_idx, refresh_immediately); + bool was_cut = object(item->obj_idx)->is_cut(); + if (!del_object(item->obj_idx, refresh_immediately)) + return; m_objects_model->Delete(m_objects_model->GetItemById(item->obj_idx)); + if (was_cut) + update_lock_icons_for_model(); } else { if (!del_subobject_from_object(item->obj_idx, item->sub_obj_idx, item->type)) - continue; + return; //continue; if (item->type&itVolume) { m_objects_model->Delete(m_objects_model->GetItemByVolumeId(item->obj_idx, item->sub_obj_idx)); // BBS @@ -3369,6 +3772,14 @@ void ObjectList::delete_from_model_and_list(const std::vector& it part_selection_changed(); } +void ObjectList::update_lock_icons_for_model() +{ + // update the icon for cut object + for (size_t obj_idx = 0; obj_idx < (*m_objects).size(); ++obj_idx) + if (!(*m_objects)[obj_idx]->is_cut()) + m_objects_model->UpdateCutObjectIcon(m_objects_model->GetItemById(int(obj_idx)), false); +} + void ObjectList::delete_all_objects_from_list() { m_prevent_list_events = true; @@ -3784,6 +4195,25 @@ bool ObjectList::is_selected(const ItemType type) const return false; } +bool ObjectList::is_connectors_item_selected() const +{ + const wxDataViewItem &item = GetSelection(); + if (item) + return m_objects_model->GetItemType(item) == itInfo && m_objects_model->GetInfoItemType(item) == InfoItemType::CutConnectors; + + return false; +} + +bool ObjectList::is_connectors_item_selected(const wxDataViewItemArray &sels) const +{ + for (auto item : sels) + if (m_objects_model->GetItemType(item) == itInfo && m_objects_model->GetInfoItemType(item) == InfoItemType::CutConnectors) + return true; + + return false; +} + + int ObjectList::get_selected_layers_range_idx() const { const wxDataViewItem& item = GetSelection(); @@ -3919,11 +4349,18 @@ void ObjectList::update_selections() else { for (auto idx : selection.get_volume_idxs()) { const auto gl_vol = selection.get_volume(idx); - if (gl_vol->volume_idx() >= 0) + if (gl_vol->volume_idx() >= 0) { // Only add GLVolumes with non-negative volume_ids. GLVolumes with negative volume ids // are not associated with ModelVolumes, but they are temporarily generated by the backend // (for example, SLA supports or SLA pad). - sels.Add(m_objects_model->GetItemByVolumeId(gl_vol->object_idx(), gl_vol->volume_idx())); + int obj_idx = gl_vol->object_idx(); + int vol_idx = gl_vol->volume_idx(); + assert(obj_idx >= 0 && vol_idx >= 0); + if (object(obj_idx)->volumes[vol_idx]->is_cut_connector()) + sels.Add(m_objects_model->GetInfoItemByType(m_objects_model->GetItemById(obj_idx), InfoItemType::CutConnectors)); + else + sels.Add(m_objects_model->GetItemByVolumeId(obj_idx, vol_idx)); + } } m_selection_mode = smVolume; } } @@ -3974,10 +4411,33 @@ void ObjectList::update_selections() if (sels.size() == 0 || m_selection_mode & smSettings) m_selection_mode = smUndef; - select_items(sels); + if (fix_cut_selection(sels) || is_connectors_item_selected(sels)) { + m_prevent_list_events = true; - // Scroll selected Item in the middle of an object list - ensure_current_item_visible(); + // If some part is selected, unselect all items except of selected parts of the current object + UnselectAll(); + SetSelections(sels); + + m_prevent_list_events = false; + + // update object selection on Plater + if (!m_prevent_canvas_selection_update) + update_selections_on_canvas(); + + // to update the toolbar and info sizer + if (!GetSelection() || m_objects_model->GetItemType(GetSelection()) == itObject || is_connectors_item_selected()) { + auto event = SimpleEvent(EVT_OBJ_LIST_OBJECT_SELECT); + event.SetEventObject(this); + wxPostEvent(this, event); + } + part_selection_changed(); + } + else { + select_items(sels); + + // Scroll selected Item in the middle of an object list + ensure_current_item_visible(); + } } void ObjectList::update_selections_on_canvas() @@ -4011,16 +4471,27 @@ void ObjectList::update_selections_on_canvas() volume_idxs.insert(volume_idxs.end(), idxs.begin(), idxs.end()); } else if (type == itInfo) { - // When selecting an info item, select one instance of the - // respective object - a gizmo may want to be opened. - int inst_idx = selection.get_instance_idx(); - int scene_obj_idx = selection.get_object_idx(); - mode = Selection::Instance; - // select first instance, unless an instance of the object is already selected - if (scene_obj_idx == -1 || inst_idx == -1 || scene_obj_idx != obj_idx) - inst_idx = 0; - std::vector idxs = selection.get_volume_idxs_from_instance(obj_idx, inst_idx); - volume_idxs.insert(volume_idxs.end(), idxs.begin(), idxs.end()); + if (m_objects_model->GetInfoItemType(item) == InfoItemType::CutConnectors) { + mode = Selection::Volume; + + // When selecting CutConnectors info item, select all object volumes, which are marked as a connector + const ModelObject *obj = object(obj_idx); + for (unsigned int vol_idx = 0; vol_idx < obj->volumes.size(); vol_idx++) + if (obj->volumes[vol_idx]->is_cut_connector()) { + std::vector idxs = selection.get_volume_idxs_from_volume(obj_idx, std::max(instance_idx, 0), vol_idx); + volume_idxs.insert(volume_idxs.end(), idxs.begin(), idxs.end()); + } + } else { + // When selecting an info item, select one instance of the + // respective object - a gizmo may want to be opened. + int inst_idx = selection.get_instance_idx(); + int scene_obj_idx = selection.get_object_idx(); + mode = Selection::Instance; + // select first instance, unless an instance of the object is already selected + if (scene_obj_idx == -1 || inst_idx == -1 || scene_obj_idx != obj_idx) inst_idx = 0; + std::vector idxs = selection.get_volume_idxs_from_instance(obj_idx, inst_idx); + volume_idxs.insert(volume_idxs.end(), idxs.begin(), idxs.end()); + } } else { @@ -4036,6 +4507,9 @@ void ObjectList::update_selections_on_canvas() if (sel_cnt == 1) { wxDataViewItem item = GetSelection(); + if (m_objects_model->GetInfoItemType(item) == InfoItemType::CutConnectors) + selection.remove_all(); + if (m_objects_model->GetItemType(item) & (itSettings | itInstanceRoot | itLayerRoot | itLayer)) add_to_selection(m_objects_model->GetParent(item), selection, instance_idx, mode); else @@ -4362,6 +4836,51 @@ void ObjectList::fix_multiselection_conflicts() m_prevent_list_events = false; } +void ObjectList::fix_cut_selection() +{ + wxDataViewItemArray sels; + GetSelections(sels); + if (fix_cut_selection(sels)) { + m_prevent_list_events = true; + + // If some part is selected, unselect all items except of selected parts of the current object + UnselectAll(); + SetSelections(sels); + + m_prevent_list_events = false; + } +} + +bool ObjectList::fix_cut_selection(wxDataViewItemArray &sels) +{ + if (wxGetApp().plater()->canvas3D()->get_gizmos_manager().get_current_type() == GLGizmosManager::Scale) { + for (const auto &item : sels) { + if (m_objects_model->GetItemType(item) & (itInstance | itObject) || + (m_objects_model->GetItemType(item) & itSettings && m_objects_model->GetItemType(m_objects_model->GetParent(item)) & itObject)) { + bool is_instance_selection = m_objects_model->GetItemType(item) & itInstance; + + int object_idx = m_objects_model->GetObjectIdByItem(item); + int inst_idx = is_instance_selection ? m_objects_model->GetInstanceIdByItem(item) : 0; + + if (auto obj = object(object_idx); obj->is_cut()) { + sels.Clear(); + + auto cut_id = obj->cut_id; + + int objects_cnt = int((*m_objects).size()); + for (int obj_idx = 0; obj_idx < objects_cnt; ++obj_idx) { + auto object = (*m_objects)[obj_idx]; + if (object->is_cut() && object->cut_id.has_same_id(cut_id)) + sels.Add(is_instance_selection ? m_objects_model->GetItemByInstanceId(obj_idx, inst_idx) : m_objects_model->GetItemById(obj_idx)); + } + return true; + } + } + } + } + return false; +} + ModelVolume* ObjectList::get_selected_model_volume() { wxDataViewItem item = GetSelection(); @@ -4690,17 +5209,20 @@ void ObjectList::fix_through_netfabb() obj_idxs.erase(obj_idxs.begin()+i); #endif // FIX_THROUGH_NETFABB_ALWAYS for (int obj_idx : obj_idxs) - model_names.push_back(object(obj_idx)->name); + if (object(obj_idx)) + model_names.push_back(object(obj_idx)->name); } else { ModelObject* obj = object(obj_idxs.front()); + if (obj) { #if !FIX_THROUGH_NETFABB_ALWAYS - for (int i = int(vol_idxs.size()) - 1; i >= 0; --i) - if (obj->get_repaired_errors_count(vol_idxs[i]) == 0) - vol_idxs.erase(vol_idxs.begin() + i); + for (int i = int(vol_idxs.size()) - 1; i >= 0; --i) + if (obj->get_repaired_errors_count(vol_idxs[i]) == 0) + vol_idxs.erase(vol_idxs.begin() + i); #endif // FIX_THROUGH_NETFABB_ALWAYS - for (int vol_idx : vol_idxs) - model_names.push_back(obj->volumes[vol_idx]->name); + for (int vol_idx : vol_idxs) + model_names.push_back(obj->volumes[vol_idx]->name); + } } auto plater = wxGetApp().plater(); @@ -4711,6 +5233,9 @@ void ObjectList::fix_through_netfabb() std::vector& succes_models, std::vector>& failed_models) { + if (!object(obj_idx)) + return false; + const std::string& model_name = model_names[model_idx]; wxString msg = _L("Repairing model object"); if (model_names.size() == 1) @@ -4838,6 +5363,7 @@ void ObjectList::msw_rescale() // BBS GetColumn(colSupportPaint)->SetWidth(3 * em); GetColumn(colColorPaint)->SetWidth(3 * em); + GetColumn(colSinking)->SetWidth(3 * em); GetColumn(colEditing )->SetWidth( 3 * em); // rescale/update existing items with bitmaps @@ -4900,6 +5426,17 @@ void ObjectList::OnEditingStarted(wxDataViewEvent &event) } return; } + else if (col == colSinking) { + Plater * plater = wxGetApp().plater(); + GLCanvas3D *cnv = plater->canvas3D(); + Plater::TakeSnapshot(plater, "Shift objects to bed"); + int obj_idx, vol_idx; + get_selected_item_indexes(obj_idx, vol_idx, item); + (*m_objects)[obj_idx]->ensure_on_bed(); + cnv->reload_scene(true, true); + update_info_items(obj_idx); + notify_instance_updated(obj_idx); + } else if (col == colEditing) { //show_context_menu(evt_context_menu); int obj_idx, vol_idx; @@ -4918,6 +5455,9 @@ void ObjectList::OnEditingStarted(wxDataViewEvent &event) if (col == colName) // TODO: for colName editing, disable shortcuts SetAcceleratorTable(wxNullAcceleratorTable); } +#ifdef __WXOSX__ + SetCustomRendererPtr(dynamic_cast(renderer)); +#endif #endif //__WXMSW__ } diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 1ec7c0bdd5..da1302ed5b 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -225,6 +225,7 @@ public: // BBS void set_color_paint_hidden(const bool hide) const; void set_support_paint_hidden(const bool hide) const; + void set_sinking_hidden(const bool hide) const; // update extruder in current config void update_filament_in_config(const wxDataViewItem& item); @@ -285,12 +286,13 @@ public: // BBS void switch_to_object_process(); int load_mesh_part(const TriangleMesh &mesh, const wxString &name, const TextInfo &text_info, bool is_temp); - void del_object(const int obj_idx, bool refresh_immediately = true); + bool del_object(const int obj_idx, bool refresh_immediately = true); void del_subobject_item(wxDataViewItem& item); void del_settings_from_config(const wxDataViewItem& parent_item); void del_instances_from_object(const int obj_idx); void del_layer_from_object(const int obj_idx, const t_layer_height_range& layer_range); void del_layers_from_object(const int obj_idx); + bool del_from_cut_object(bool is_connector, bool is_model_part = false, bool is_negative_volume = false); bool del_subobject_from_object(const int obj_idx, const int idx, const int type); void del_info_item(const int obj_idx, InfoItemType type); void split(); @@ -309,6 +311,12 @@ public: bool can_merge_to_multipart_object() const; bool can_merge_to_single_object() const; + bool has_selected_cut_object() const; + void invalidate_cut_info_for_selection(); + void invalidate_cut_info_for_object(int obj_idx); + void delete_all_connectors_for_selection(); + void delete_all_connectors_for_object(int obj_idx); + wxPoint get_mouse_position_in_control() const { return wxGetMousePosition() - this->GetScreenPosition(); } int get_selected_obj_idx() const; ModelConfig& get_item_config(const wxDataViewItem& item) const; @@ -318,6 +326,9 @@ public: // Add object to the list void add_object_to_list(size_t obj_idx, bool call_selection_changed = true, bool notify_partplate = true); + // Add object's volumes to the list + // Return selected items, if add_to_selection is defined + wxDataViewItemArray add_volumes_to_object_in_list(size_t obj_idx, std::function add_to_selection = nullptr); // Delete object from the list void delete_object_from_list(); void delete_object_from_list(const size_t obj_idx); @@ -325,6 +336,7 @@ public: void delete_instance_from_list(const size_t obj_idx, const size_t inst_idx); void delete_from_model_and_list(const ItemType type, const int obj_idx, const int sub_obj_idx); void delete_from_model_and_list(const std::vector& items_for_delete); + void update_lock_icons_for_model(); // Delete all objects from the list void delete_all_objects_from_list(); // Increase instances count @@ -367,6 +379,8 @@ public: void init(); bool multiple_selection() const ; bool is_selected(const ItemType type) const; + bool is_connectors_item_selected() const; + bool is_connectors_item_selected(const wxDataViewItemArray &sels) const; int get_selected_layers_range_idx() const; void set_selected_layers_range_idx(const int range_idx) { m_selected_layers_range_idx = range_idx; } void set_selection_mode(SELECTION_MODE mode) { m_selection_mode = mode; } @@ -384,6 +398,9 @@ public: bool check_last_selection(wxString& msg_str); // correct current selections to avoid of the possible conflicts void fix_multiselection_conflicts(); + // correct selection in respect to the cut_id if any exists + void fix_cut_selection(); + bool fix_cut_selection(wxDataViewItemArray &sels); ModelVolume* get_selected_model_volume(); void change_part_type(); diff --git a/src/slic3r/GUI/GUI_ObjectSettings.cpp b/src/slic3r/GUI/GUI_ObjectSettings.cpp index d6662b8b4d..f11085543e 100644 --- a/src/slic3r/GUI/GUI_ObjectSettings.cpp +++ b/src/slic3r/GUI/GUI_ObjectSettings.cpp @@ -26,7 +26,9 @@ namespace GUI OG_Settings::OG_Settings(wxWindow* parent, const bool staticbox) : m_parent(parent) { - wxString title = staticbox ? " " : ""; // temporary workaround - #ys_FIXME + //BBS + wxString title = ""; + //wxString title = staticbox ? " " : ""; // temporary workaround - #ys_FIXME m_og = std::make_shared(parent, title); } @@ -106,13 +108,14 @@ bool ObjectSettings::update_settings_list() auto opt_key = (line.get_options())[0].opt_id; //we assume that we have one option per line auto btn = new ScalableButton(parent, wxID_ANY, m_bmp_delete); + btn->SetBackgroundColour(parent->GetBackgroundColour()); btn->SetToolTip(_(L("Remove parameter"))); btn->SetBitmapFocus(m_bmp_delete_focus.bmp()); btn->SetBitmapHover(m_bmp_delete_focus.bmp()); btn->Bind(wxEVT_BUTTON, [opt_key, config, this](wxEvent &event) { - wxGetApp().plater()->take_snapshot(from_u8(boost::format(("Delete Option %s") % opt_key).str())); + wxGetApp().plater()->take_snapshot(from_u8((boost::format("Delete Option %s") % opt_key).str()).ToStdString()); config->erase(opt_key); wxGetApp().obj_list()->changed_object(); wxTheApp->CallAfter([this]() { @@ -159,7 +162,7 @@ bool ObjectSettings::update_settings_list() for (auto& opt : cat.second) optgroup->get_field(opt)->m_on_change = [optgroup](const std::string& opt_id, const boost::any& value) { // first of all take a snapshot and then change value in configuration - wxGetApp().plater()->take_snapshot(from_u8((boost::format("Change Option %s")% opt_id).str())); + wxGetApp().plater()->take_snapshot(from_u8((boost::format("Change Option %s")% opt_id).str()).ToStdString()); optgroup->on_change_OG(opt_id, value); }; @@ -183,7 +186,6 @@ bool ObjectSettings::update_settings_list() } #else - bool ObjectSettings::update_settings_list() { if (!wxGetApp().is_editor()) @@ -198,51 +200,74 @@ bool ObjectSettings::update_settings_list() objects_ctrl->GetSelections(items); std::map object_configs; - bool is_object_settings = true; - bool is_volume_settings = true; + bool is_object_settings = false; + bool is_volume_settings = false; + bool is_layer_range_settings = false; + bool is_layer_root = false; ModelObject * parent_object = nullptr; for (auto item : items) { auto type = objects_model->GetItemType(item); - if (type != itObject && type != itVolume) { + if (type != itObject && type != itVolume && type != itLayerRoot && type != itLayer) { continue; } const int obj_idx = objects_model->GetObjectIdByItem(item); assert(obj_idx >= 0); auto object = wxGetApp().model().objects[obj_idx]; if (type == itObject) { - if (!is_object_settings) - return false; - is_volume_settings = false; + is_object_settings = true; object_configs.emplace(object, &object->config); - } else { - if (!is_volume_settings) - return false; + } + else if(type == itVolume){ + is_volume_settings = true; if (parent_object && parent_object != object) return false; parent_object = object; - is_object_settings = false; const int vol_idx = objects_model->GetVolumeIdByItem(item); assert(vol_idx >= 0); auto volume = object->volumes[vol_idx]; object_configs.emplace(volume, &volume->config); } + else if(type == itLayer){ + is_layer_range_settings = true; + if (parent_object && parent_object != object) + return false; + parent_object = object; + + t_layer_height_range height_range = objects_model->GetLayerRangeByItem(item); + object_configs.emplace( (ObjectBase*)(&object->layer_config_ranges.at(height_range)), &object->layer_config_ranges.at(height_range) ); + } + else if (type == itLayerRoot) { + is_layer_root = true; + } } auto tab_object = dynamic_cast(wxGetApp().get_model_tab()); auto tab_volume = dynamic_cast(wxGetApp().get_model_tab(true)); + auto tab_layer = dynamic_cast(wxGetApp().get_layer_tab()); - if (is_volume_settings == is_object_settings) { - tab_object->set_model_config({}); - tab_volume->set_model_config({}); - m_tab_active = nullptr; - } else if (is_volume_settings) { - tab_object->set_model_config({{parent_object, &parent_object->config}}); - tab_volume->set_model_config(object_configs); - m_tab_active = tab_volume; - } else if (is_object_settings) { + if (is_object_settings) { tab_object->set_model_config(object_configs); tab_volume->set_model_config({}); + tab_layer->set_model_config({}); m_tab_active = tab_object; + } + else if (is_volume_settings) { + tab_object->set_model_config({ {parent_object, &parent_object->config} }); + tab_volume->set_model_config(object_configs); + tab_layer->set_model_config({}); + m_tab_active = tab_volume; + } + else if (is_layer_range_settings) { + tab_object->set_model_config({ {parent_object, &parent_object->config} }); + tab_volume->set_model_config({}); + tab_layer->set_model_config(object_configs); + m_tab_active = tab_layer; + } + else { + tab_object->set_model_config({}); + tab_volume->set_model_config({}); + tab_layer->set_model_config({}); + m_tab_active = nullptr; } ((ParamsPanel*) tab_object->GetParent())->set_active_tab(nullptr); return true; diff --git a/src/slic3r/GUI/GUI_ObjectTable.cpp b/src/slic3r/GUI/GUI_ObjectTable.cpp index 301ae99126..630688543d 100644 --- a/src/slic3r/GUI/GUI_ObjectTable.cpp +++ b/src/slic3r/GUI/GUI_ObjectTable.cpp @@ -1384,11 +1384,20 @@ wxString ObjectGridTable::GetValue (int row, int col) } else { try { - ConfigOptionString& option_value = dynamic_cast(option); - if (grid_row->row_type == row_volume) + if (grid_row->row_type == row_volume){ + ConfigOptionString& option_value = dynamic_cast(option); return GUI::from_u8(std::string(" ") + option_value.value); - else - return GUI::from_u8(option_value.value); + } + else { + if (option.type() == coInt) { + ConfigOptionInt& option_value = dynamic_cast(option); + return GUI::from_u8(wxString::Format("%d",option_value.value).ToStdString()); + }else { + ConfigOptionString& option_value = dynamic_cast(option); + return GUI::from_u8(option_value.value); + } + + } } catch(...) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format("row %1%, col %2%, type %3% ")%row %col %grid_col->type; @@ -2928,11 +2937,11 @@ void ObjectTablePanel::load_data() m_object_grid->SetCellRenderer(row, col, new GridCellIconRenderer()); m_object_grid->SetReadOnly(row, col); } - else if (grid_col->b_for_object && (grid_row->row_type == ObjectGridTable::row_volume)) { - m_object_grid->SetReadOnly(row, col); - m_object_grid->SetCellEditor(row, col, new wxGridCellAutoWrapStringEditor()); - m_object_grid->SetCellRenderer(row, col, new wxGridCellAutoWrapStringRenderer()); - } + //else if (grid_col->b_for_object && (grid_row->row_type == ObjectGridTable::row_volume)) { + // /*m_object_grid->SetReadOnly(row, col); + // m_object_grid->SetCellEditor(row, col, new wxGridCellAutoWrapStringEditor()); + // m_object_grid->SetCellRenderer(row, col, new wxGridCellAutoWrapStringRenderer());*/ + //} else { if (!grid_col->b_editable) m_object_grid->SetReadOnly(row, col); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 64f0fc4a0f..29c636efbb 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -581,12 +581,13 @@ void Preview::update_layers_slider(const std::vector& layers_z, bool kee } } m_layers_slider->SetSelectionSpan(idx_low, idx_high); - m_layers_slider->SetTicksValues(ticks_info_from_curr_plate); auto curr_plate = wxGetApp().plater()->get_partplate_list().get_curr_plate(); auto curr_print_seq = curr_plate->get_real_print_seq(); bool sequential_print = (curr_print_seq == PrintSequence::ByObject); m_layers_slider->SetDrawMode(sequential_print); + + m_layers_slider->SetTicksValues(ticks_info_from_curr_plate); auto print_mode_stat = m_gcode_result->print_statistics.modes.front(); m_layers_slider->SetLayersTimes(print_mode_stat.layers_times, print_mode_stat.time); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.cpp index c8e9e8470e..dbc8281bf6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.cpp @@ -19,6 +19,56 @@ namespace Slic3r { namespace GUI { + +const double units_in_to_mm = 25.4; +const double units_mm_to_in = 1 / units_in_to_mm; + +const int c_connectors_group_id = 4; +const float UndefFloat = -999.f; + +// connector colors + +using ColorRGBA = std::array; + +static const ColorRGBA BLACK() { return {0.0f, 0.0f, 0.0f, 1.0f}; } +static const ColorRGBA BLUE() { return {0.0f, 0.0f, 1.0f, 1.0f}; } +static const ColorRGBA BLUEISH() { return {0.5f, 0.5f, 1.0f, 1.0f}; } +static const ColorRGBA CYAN() { return {0.0f, 1.0f, 1.0f, 1.0f}; } +static const ColorRGBA DARK_GRAY() { return {0.25f, 0.25f, 0.25f, 1.0f}; } +static const ColorRGBA DARK_YELLOW() { return {0.5f, 0.5f, 0.0f, 1.0f}; } +static const ColorRGBA GRAY() { return {0.5f, 0.5f, 0.5f, 1.0f}; } +static const ColorRGBA GREEN() { return {0.0f, 1.0f, 0.0f, 1.0f}; } +static const ColorRGBA GREENISH() { return {0.5f, 1.0f, 0.5f, 1.0f}; } +static const ColorRGBA LIGHT_GRAY() { return {0.75f, 0.75f, 0.75f, 1.0f}; } +static const ColorRGBA MAGENTA() { return {1.0f, 0.0f, 1.0f, 1.0f}; } +static const ColorRGBA ORANGE() { return {0.923f, 0.504f, 0.264f, 1.0f}; } +static const ColorRGBA RED() { return {1.0f, 0.0f, 0.0f, 1.0f}; } +static const ColorRGBA REDISH() { return {1.0f, 0.5f, 0.5f, 1.0f}; } +static const ColorRGBA YELLOW() { return {1.0f, 1.0f, 0.0f, 1.0f}; } +static const ColorRGBA WHITE() { return {1.0f, 1.0f, 1.0f, 1.0f}; } + +static const ColorRGBA PLAG_COLOR = YELLOW(); +static const ColorRGBA DOWEL_COLOR = DARK_YELLOW(); +static const ColorRGBA HOVERED_PLAG_COLOR = CYAN(); +static const ColorRGBA HOVERED_DOWEL_COLOR = {0.0f, 0.5f, 0.5f, 1.0f}; +static const ColorRGBA SELECTED_PLAG_COLOR = GRAY(); +static const ColorRGBA SELECTED_DOWEL_COLOR = GRAY(); // DARK_GRAY(); +static const ColorRGBA CONNECTOR_DEF_COLOR = {1.0f, 1.0f, 1.0f, 0.5f}; +static const ColorRGBA CONNECTOR_ERR_COLOR = {1.0f, 0.3f, 0.3f, 0.5f}; +static const ColorRGBA HOVERED_ERR_COLOR = {1.0f, 0.3f, 0.3f, 1.0f}; + +static Vec3d rotate_vec3d_around_vec3d_with_rotate_matrix( + const Vec3d& rotate_point, + const Vec3d& origin_point, + const Transform3d& rotate_matrix) +{ + Transform3d translate_to_point = Transform3d::Identity(); + translate_to_point.translate(origin_point); + Transform3d translate_to_zero = Transform3d::Identity(); + translate_to_zero.translate(-origin_point); + return (translate_to_point * rotate_matrix * translate_to_zero) * rotate_point; +} + static inline void rotate_point_2d(double& x, double& y, const double c, const double s) { double xold = x; @@ -63,11 +113,13 @@ GLGizmoAdvancedCut::GLGizmoAdvancedCut(GLCanvas3D& parent, const std::string& ic , m_last_active_id(0) , m_keep_upper(true) , m_keep_lower(true) - , m_rotate_lower(false) , m_cut_to_parts(false) , m_do_segment(false) , m_segment_smoothing_alpha(0.5) , m_segment_number(5) + , m_connector_type(CutConnectorType::Plug) + , m_connector_style(size_t(CutConnectorStyle::Prizm)) + , m_connector_shape_id(size_t(CutConnectorShape::Circle)) { for (int i = 0; i < 4; i++) m_cut_plane_points[i] = { 0., 0., 0. }; @@ -79,11 +131,140 @@ GLGizmoAdvancedCut::GLGizmoAdvancedCut(GLCanvas3D& parent, const std::string& ic m_buffered_rotation.setZero(); } +bool GLGizmoAdvancedCut::gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_position, bool shift_down, bool alt_down, bool control_down) +{ + CutConnectors &connectors = m_c->selection_info()->model_object()->cut_connectors; + + if (shift_down && !m_connectors_editing && + (action == SLAGizmoEventType::LeftDown || action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::Dragging)) { + process_cut_line(action, mouse_position); + return true; + } + + if (action == SLAGizmoEventType::LeftDown) { + if (!m_connectors_editing) + return false; + + if (m_hover_id != -1) { + start_dragging(); + return true; + } + + if (shift_down || alt_down) { + // left down with shift - show the selection rectangle: + //if (m_hover_id == -1) + // m_selection_rectangle.start_dragging(mouse_position, shift_down ? GLSelectionRectangle::EState::Select : GLSelectionRectangle::EState::Deselect); + } else { + // If there is no selection and no hovering, add new point + if (m_hover_id == -1 && !shift_down && !alt_down) + add_connector(connectors, mouse_position); + //m_ldown_mouse_position = mouse_position; + } + return true; + } + else if (action == SLAGizmoEventType::LeftUp) { + if (m_hover_id == -1 && !shift_down && !alt_down) + unselect_all_connectors(); + + is_selection_changed(alt_down, shift_down); + return true; + } + else if (action == SLAGizmoEventType::RightDown) { + if (m_hover_id < c_connectors_group_id) + return false; + + unselect_all_connectors(); + select_connector(m_hover_id - c_connectors_group_id, true); + return delete_selected_connectors(); + } + else if (action == SLAGizmoEventType::RightUp) { + // catch right click event + return true; + } + + return false; +} + +bool GLGizmoAdvancedCut::on_key(wxKeyEvent &evt) +{ + bool ctrl_down = evt.GetModifiers() & wxMOD_CONTROL; + + if (evt.GetKeyCode() == WXK_DELETE) { + return delete_selected_connectors(); + } + else if (ctrl_down + && (evt.GetKeyCode() == 'A' || evt.GetKeyCode() == 'a')) + { + select_all_connectors(); + return true; + } + return false; +} + std::string GLGizmoAdvancedCut::get_tooltip() const { return ""; } +BoundingBoxf3 GLGizmoAdvancedCut::bounding_box() const +{ + BoundingBoxf3 ret; + const Selection & selection = m_parent.get_selection(); + const Selection::IndicesList &idxs = selection.get_volume_idxs(); + for (unsigned int i : idxs) { + const GLVolume *volume = selection.get_volume(i); + // respect just to the solid parts for FFF and ignore pad and supports for SLA + if (!volume->is_modifier && !volume->is_sla_pad() && !volume->is_sla_support()) ret.merge(volume->transformed_convex_hull_bounding_box()); + } + return ret; +} + +bool GLGizmoAdvancedCut::is_looking_forward() const +{ + const Camera &camera = wxGetApp().plater()->get_camera(); + const double dot = camera.get_dir_forward().dot(m_cut_plane_normal); + return dot < 0.05; +} + +// Unprojects the mouse position on the mesh and saves hit point and normal of the facet into pos_and_normal +// Return false if no intersection was found, true otherwise. +bool GLGizmoAdvancedCut::unproject_on_cut_plane(const Vec2d &mouse_pos, Vec3d &pos, Vec3d &pos_world) +{ + const float sla_shift = m_c->selection_info()->get_sla_shift(); + + const ModelObject * mo = m_c->selection_info()->model_object(); + const ModelInstance *mi = mo->instances[m_c->selection_info()->get_active_instance()]; + const Camera & camera = wxGetApp().plater()->get_camera(); + + // Calculate intersection with the clipping plane. + const ClippingPlane *cp = m_c->object_clipper()->get_clipping_plane(); + Vec3d point; + Vec3d direction; + Vec3d hit; + MeshRaycaster::line_from_mouse_pos_static(mouse_pos, Transform3d::Identity(), camera, point, direction); + Vec3d normal = -cp->get_normal().cast(); + double den = normal.dot(direction); + if (den != 0.) { + double t = (-cp->get_offset() - normal.dot(point)) / den; + hit = (point + t * direction); + } else + return false; + + if (!m_c->object_clipper()->is_projection_inside_cut(hit)) + return false; + + // recalculate hit to object's local position + Vec3d hit_d = hit; + hit_d -= mi->get_offset(); + hit_d[Z] -= sla_shift; + + // Return both the point and the facet normal. + pos = hit_d; + pos_world = hit; + + return true; +} + void GLGizmoAdvancedCut::update_plane_points() { Vec3d plane_center = get_plane_center(); @@ -171,6 +352,10 @@ void GLGizmoAdvancedCut::reset_all() m_keep_upper = true; m_keep_lower = true; m_cut_to_parts = false; + m_place_on_cut_upper = true; + m_place_on_cut_lower = false; + m_rotate_upper = false; + m_rotate_lower = false; } bool GLGizmoAdvancedCut::on_init() @@ -179,6 +364,20 @@ bool GLGizmoAdvancedCut::on_init() return false; m_shortcut_key = WXK_CONTROL_C; + + // initiate info shortcuts + const wxString ctrl = GUI::shortkey_ctrl_prefix(); + const wxString alt = GUI::shortkey_alt_prefix(); + const wxString shift = "Shift+"; + + m_shortcuts.push_back(std::make_pair(_L("Left click"), _L("Add connector"))); + m_shortcuts.push_back(std::make_pair(_L("Right click"), _L("Remove connector"))); + m_shortcuts.push_back(std::make_pair(_L("Drag"), _L("Move connector"))); + m_shortcuts.push_back(std::make_pair(shift + _L("Left click"), _L("Add connector to selection"))); + m_shortcuts.push_back(std::make_pair(alt + _L("Left click"), _L("Remove connector from selection"))); + m_shortcuts.push_back(std::make_pair(ctrl + "A", _L("Select all connectors"))); + + init_connector_shapes(); return true; } @@ -187,14 +386,33 @@ std::string GLGizmoAdvancedCut::on_get_name() const return (_(L("Cut"))).ToUTF8().data(); } +void GLGizmoAdvancedCut::on_load(cereal::BinaryInputArchive &ar) +{ + ar(m_keep_upper, m_keep_lower, m_rotate_lower, m_rotate_upper, m_connectors_editing, + m_cut_plane_points[0], m_cut_plane_points[1], m_cut_plane_points[2], m_cut_plane_points[3]); + + m_parent.request_extra_frame(); +} + +void GLGizmoAdvancedCut::on_save(cereal::BinaryOutputArchive &ar) const +{ + ar(m_keep_upper, m_keep_lower, m_rotate_lower, m_rotate_upper, m_connectors_editing, + m_cut_plane_points[0], m_cut_plane_points[1], m_cut_plane_points[2], m_cut_plane_points[3]); +} + void GLGizmoAdvancedCut::on_set_state() { GLGizmoRotate3D::on_set_state(); // Reset m_cut_z on gizmo activation if (get_state() == On) { + m_connectors_editing = false; reset_cut_plane(); } + else if (get_state() == Off) { + clear_selection(); + m_c->object_clipper()->release(); + } } bool GLGizmoAdvancedCut::on_is_activable() const @@ -203,6 +421,14 @@ bool GLGizmoAdvancedCut::on_is_activable() const return selection.is_single_full_instance() && !selection.is_wipe_tower(); } +CommonGizmosDataID GLGizmoAdvancedCut::on_get_requirements() const +{ + return CommonGizmosDataID(int(CommonGizmosDataID::SelectionInfo) + | int(CommonGizmosDataID::InstancesHider) + | int(CommonGizmosDataID::Raycaster) + | int(CommonGizmosDataID::ObjectClipper)); +} + void GLGizmoAdvancedCut::on_start_dragging() { for (auto gizmo : m_gizmos) { @@ -220,6 +446,18 @@ void GLGizmoAdvancedCut::on_start_dragging() m_start_movement = m_movement; m_start_height = m_height; m_drag_pos = m_move_grabber.center; + + if (m_hover_id >= c_connectors_group_id) + Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Move connector"); +} + +void GLGizmoAdvancedCut::on_stop_dragging() +{ + if (m_hover_id == X || m_hover_id == Y || m_hover_id == Z) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Rotate cut plane"); + } else if (m_hover_id == c_connectors_group_id - 1) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Move cut plane"); + } } void GLGizmoAdvancedCut::on_update(const UpdateData& data) @@ -243,129 +481,35 @@ void GLGizmoAdvancedCut::on_update(const UpdateData& data) Vec3d plane_normal = get_plane_normal(); m_height = m_start_height + plane_normal(2) * move; } + + // dragging connectors + if (m_connectors_editing && m_hover_id >= c_connectors_group_id) { + CutConnectors &connectors = m_c->selection_info()->model_object()->cut_connectors; + Vec3d pos; + Vec3d pos_world; + + if (unproject_on_cut_plane(data.mouse_pos.cast(), pos, pos_world)) { + connectors[m_hover_id - c_connectors_group_id].pos = pos; + } + } } void GLGizmoAdvancedCut::on_render() { - const Selection& selection = m_parent.get_selection(); - const BoundingBoxf3& box = selection.get_bounding_box(); - // box center is the coord of object in the world coordinate - Vec3d object_offset = box.center(); - // plane points is in object coordinate - Vec3d plane_center = get_plane_center(); + update_clipper(); + if (m_connectors_editing) { + render_connectors(); + } + + // render_clipper_cut for get the cut plane result + render_clipper_cut(); - // rotate plane - std::array plane_points_rot; - { - for (int i = 0; i < plane_points_rot.size(); i++) { - plane_points_rot[i] = m_cut_plane_points[i] - plane_center; - } - - if (m_rotation(0) > EPSILON) - rotate_x_3d(plane_points_rot, m_rotation(0)); - if (m_rotation(1) > EPSILON) - rotate_y_3d(plane_points_rot, m_rotation(1)); - if (m_rotation(2) > EPSILON) - rotate_z_3d(plane_points_rot, m_rotation(2)); - - for (int i = 0; i < plane_points_rot.size(); i++) { - plane_points_rot[i] += plane_center; - } + if(!m_connectors_editing) { + check_conflict_for_all_connectors(); + render_cut_plane_and_grabbers(); } - // move plane - Vec3d plane_normal_rot = calc_plane_normal(plane_points_rot); - for (int i = 0; i < plane_points_rot.size(); i++) { - plane_points_rot[i] += plane_normal_rot * m_movement; - } - - // transfer from object coordindate to the world coordinate - for (Vec3d& point : plane_points_rot) { - point += object_offset; - } - - // draw plane - glsafe(::glEnable(GL_DEPTH_TEST)); - glsafe(::glDisable(GL_CULL_FACE)); - glsafe(::glEnable(GL_BLEND)); - glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - - ::glBegin(GL_QUADS); - ::glColor4f(0.8f, 0.8f, 0.8f, 0.5f); - for (const Vec3d& point : plane_points_rot) { - ::glVertex3f(point(0), point(1), point(2)); - } - glsafe(::glEnd()); - - glsafe(::glEnable(GL_CULL_FACE)); - glsafe(::glDisable(GL_BLEND)); - - // Draw the grabber and the connecting line - Vec3d plane_center_rot = calc_plane_center(plane_points_rot); - m_move_grabber.center = plane_center_rot + plane_normal_rot * Offset; - //m_move_grabber.angles = m_current_base_rotation + m_rotation; - - glsafe(::glDisable(GL_DEPTH_TEST)); - glsafe(::glLineWidth(m_hover_id != -1 ? 2.0f : 1.5f)); - glsafe(::glColor3f(1.0, 1.0, 0.0)); - glLineStipple(1, 0x0FFF); - glEnable(GL_LINE_STIPPLE); - ::glBegin(GL_LINES); - ::glVertex3dv(plane_center_rot.data()); - ::glVertex3dv(m_move_grabber.center.data()); - glsafe(::glEnd()); - glDisable(GL_LINE_STIPPLE); - - //std::copy(std::begin(GrabberColor), std::end(GrabberColor), m_move_grabber.color); - //m_move_grabber.color = GrabberColor; - //m_move_grabber.hover_color = GrabberHoverColor; - //m_move_grabber.render(m_hover_id == get_group_id(), (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0)); - bool hover = (m_hover_id == get_group_id()); - std::array render_color; - if (hover) { - render_color = GrabberHoverColor; - } - else - render_color = GrabberColor; - - const GLModel& cube = m_move_grabber.get_cube(); - //BBS set to fixed size grabber - //float fullsize = 2 * (dragging ? get_dragging_half_size(size) : get_half_size(size)); - float fullsize = 8.0f; - if (GLGizmoBase::INV_ZOOM > 0) { - fullsize = m_move_grabber.FixedGrabberSize * GLGizmoBase::INV_ZOOM; - } - - const_cast(&cube)->set_color(-1, render_color); - - glsafe(::glPushMatrix()); - glsafe(::glTranslated(m_move_grabber.center.x(), m_move_grabber.center.y(), m_move_grabber.center.z())); - - if (m_rotation(0) > EPSILON) - glsafe(::glRotated(Geometry::rad2deg(m_rotation(0)), 1.0, 0.0, 0.0)); - if (m_rotation(1) > EPSILON) - glsafe(::glRotated(Geometry::rad2deg(m_rotation(1)), 0.0, 1.0, 0.0)); - if (m_rotation(2) > EPSILON) - glsafe(::glRotated(Geometry::rad2deg(m_rotation(2)), 0.0, 0.0, 1.0)); - for (int index = 0; index < m_rotate_cmds.size(); index ++) - { - Rotate_data& data = m_rotate_cmds[index]; - if (data.ax == X) - glsafe(::glRotated(Geometry::rad2deg(data.angle), 1.0, 0.0, 0.0)); - else if (data.ax == Y) - glsafe(::glRotated(Geometry::rad2deg(data.angle), 0.0, 1.0, 0.0)); - else if (data.ax == Z) - glsafe(::glRotated(Geometry::rad2deg(data.angle), 0.0, 0.0, 1.0)); - } - //glsafe(::glRotated(Geometry::rad2deg(angles.z()), 0.0, 0.0, 1.0)); - //glsafe(::glRotated(Geometry::rad2deg(angles.y()), 0.0, 1.0, 0.0)); - //glsafe(::glRotated(Geometry::rad2deg(angles.x()), 1.0, 0.0, 0.0)); - glsafe(::glScaled(fullsize, fullsize, fullsize)); - cube.render(); - glsafe(::glPopMatrix()); - - // Should be placed at last, because GLGizmoRotate3D clears depth buffer - GLGizmoRotate3D::on_render(); + render_cut_line(); } void GLGizmoAdvancedCut::on_render_for_picking() @@ -387,171 +531,95 @@ void GLGizmoAdvancedCut::on_render_for_picking() m_move_grabber.color[2] = color[2]; m_move_grabber.color[3] = color[3]; m_move_grabber.render_for_picking(mean_size); + + glsafe(::glEnable(GL_DEPTH_TEST)); + auto inst_id = m_c->selection_info()->get_active_instance(); + if (inst_id < 0) + return; + + const ModelObject *mo = m_c->selection_info()->model_object(); + const ModelInstance *mi = mo->instances[inst_id]; + const Vec3d & instance_offset = mi->get_offset(); + const double sla_shift = double(m_c->selection_info()->get_sla_shift()); + + const CutConnectors &connectors = mo->cut_connectors; + for (int i = 0; i < connectors.size(); ++i) { + CutConnector connector = connectors[i]; + Vec3d pos = connector.pos + instance_offset + sla_shift * Vec3d::UnitZ(); + float height = connector.height; + + const Camera &camera = wxGetApp().plater()->get_camera(); + if (connector.attribs.type == CutConnectorType::Dowel && connector.attribs.style == CutConnectorStyle::Prizm) { + pos -= height * m_cut_plane_normal; + height *= 2; + } else if (!is_looking_forward()) + pos -= 0.05 * m_cut_plane_normal; + + Transform3d translate_tf = Transform3d::Identity(); + translate_tf.translate(pos); + + Transform3d scale_tf = Transform3d::Identity(); + scale_tf.scale(Vec3f(connector.radius, connector.radius, height).cast()); + + const Transform3d view_model_matrix = translate_tf * m_rotate_matrix * scale_tf; + + + std::array color = picking_color_component(i+1); + render_connector_model(m_shapes[connectors[i].attribs], color, view_model_matrix, true); + } } void GLGizmoAdvancedCut::on_render_input_window(float x, float y, float bottom_limit) { - //float unit_size = m_imgui->get_style_scaling() * 48.0f; - float space_size = m_imgui->get_style_scaling() * 8; - float movement_cap = m_imgui->calc_text_size(_L("Movement:")).x; - float rotate_cap = m_imgui->calc_text_size(_L("Rotate")).x; - float caption_size = std::max(movement_cap, rotate_cap) + 2 * space_size; - bool imperial_units = wxGetApp().app_config->get("use_inches") == "1"; - unsigned int current_active_id = ImGui::GetActiveID(); - - Vec3d rotation = {Geometry::rad2deg(m_rotation(0)), Geometry::rad2deg(m_rotation(1)), Geometry::rad2deg(m_rotation(2))}; - char buf[3][64]; - float buf_size[3]; - float vec_max = 0, unit_size = 0; - for (int i = 0; i < 3; i++) { - ImGui::DataTypeFormatString(buf[i], IM_ARRAYSIZE(buf[i]), ImGuiDataType_Double, (void *) &rotation[i], "%.2f"); - buf_size[i] = ImGui::CalcTextSize(buf[i]).x; - vec_max = std::max(buf_size[i], vec_max); - } - - float buf_size_max = ImGui::CalcTextSize("-100.00").x ; - if (vec_max < buf_size_max){ - unit_size = buf_size_max + ImGui::GetStyle().FramePadding.x * 2.0f; - } else { - unit_size = vec_max + ImGui::GetStyle().FramePadding.x * 2.0f; - } - GizmoImguiSetNextWIndowPos(x, y, ImGuiCond_Always, 0.0f, 0.0f); - ImGuiWrapper::push_toolbar_style(m_parent.get_scale()); + GizmoImguiBegin(on_get_name(), + ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar); - GizmoImguiBegin(on_get_name(), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar); - - ImGui::PushItemWidth(caption_size); - ImGui::Dummy(ImVec2(caption_size, -1)); - ImGui::SameLine(caption_size + 1 * space_size); - ImGui::PushItemWidth(unit_size); - ImGui::TextAlignCenter("X"); - ImGui::SameLine(caption_size + 1 * unit_size + 2 * space_size); - ImGui::PushItemWidth(unit_size); - ImGui::TextAlignCenter("Y"); - ImGui::SameLine(caption_size + 2 * unit_size + 3 * space_size); - ImGui::PushItemWidth(unit_size); - ImGui::TextAlignCenter("Z"); - - ImGui::AlignTextToFramePadding(); - - // Rotation input box - ImGui::PushItemWidth(caption_size); - m_imgui->text(_L("Rotation") + " "); - ImGui::SameLine(caption_size + 1 * space_size); - ImGui::PushItemWidth(unit_size); - ImGui::BBLInputDouble("##cut_rotation_x", &rotation[0], 0.0f, 0.0f, "%.2f"); - ImGui::SameLine(caption_size + 1 * unit_size + 2 * space_size); - ImGui::PushItemWidth(unit_size); - ImGui::BBLInputDouble("##cut_rotation_y", &rotation[1], 0.0f, 0.0f, "%.2f"); - ImGui::SameLine(caption_size + 2 * unit_size + 3 * space_size); - ImGui::PushItemWidth(unit_size); - ImGui::BBLInputDouble("##cut_rotation_z", &rotation[2], 0.0f, 0.0f, "%.2f"); - if (current_active_id != m_last_active_id) { - if (std::abs(Geometry::rad2deg(m_rotation(0)) - m_buffered_rotation(0)) > EPSILON || - std::abs(Geometry::rad2deg(m_rotation(1)) - m_buffered_rotation(1)) > EPSILON || - std::abs(Geometry::rad2deg(m_rotation(2)) - m_buffered_rotation(2)) > EPSILON) - { - m_rotation = m_buffered_rotation; - m_buffered_rotation.setZero(); - update_plane_points(); - m_parent.post_event(SimpleEvent(wxEVT_PAINT)); - } - } - else { - m_buffered_rotation(0) = Geometry::deg2rad(rotation(0)); - m_buffered_rotation(1) = Geometry::deg2rad(rotation(1)); - m_buffered_rotation(2) = Geometry::deg2rad(rotation(2)); + if (m_connectors_editing) { + init_connectors_input_window_data(); + render_connectors_input_window(x, y, bottom_limit); } + else + render_cut_plane_input_window(x, y, bottom_limit); - ImGui::Separator(); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4.0f, 10.0f)); - // Movement input box - double movement = m_movement; - ImGui::PushItemWidth(caption_size); - ImGui::AlignTextToFramePadding(); - m_imgui->text(_L("Movement") + " "); - ImGui::SameLine(caption_size + 1 * space_size); - ImGui::PushItemWidth(3 * unit_size + 2 * space_size); - ImGui::BBLInputDouble("##cut_movement", &movement, 0.0f, 0.0f, "%.2f"); - if (current_active_id != m_last_active_id) { - if (std::abs(m_buffered_movement - m_movement) > EPSILON) { - m_movement = m_buffered_movement; - m_buffered_movement = 0.0; + render_input_window_warning(); - // update absolute height - Vec3d plane_normal = get_plane_normal(); - m_height_delta = plane_normal(2) * m_movement; - m_height += m_height_delta; - m_buffered_height = m_height; - - update_plane_points(); - m_parent.post_event(SimpleEvent(wxEVT_PAINT)); - } - } else { - m_buffered_movement = movement; - } - - // height input box - double height = m_height; - ImGui::PushItemWidth(caption_size); - ImGui::AlignTextToFramePadding(); - // only allow setting height when cut plane is horizontal - Vec3d plane_normal = get_plane_normal(); - m_imgui->disabled_begin(std::abs(plane_normal(0)) > EPSILON || std::abs(plane_normal(1)) > EPSILON); - m_imgui->text(_L("Height") + " "); - ImGui::SameLine(caption_size + 1 * space_size); - ImGui::PushItemWidth(3 * unit_size + 2 * space_size); - ImGui::BBLInputDouble("##cut_height", &height, 0.0f, 0.0f, "%.2f"); - if (current_active_id != m_last_active_id) { - if (std::abs(m_buffered_height - m_height) > EPSILON) { - m_height_delta = m_buffered_height - m_height; - m_height = m_buffered_height; - update_plane_points(); - m_parent.post_event(SimpleEvent(wxEVT_PAINT)); - } - } - else { - m_buffered_height = height; - } - ImGui::PopStyleVar(1); - m_imgui->disabled_end(); - ImGui::Separator(); - // Part selection - m_imgui->bbl_checkbox(_L("Keep upper part"), m_keep_upper); - m_imgui->bbl_checkbox(_L("Keep lower part"), m_keep_lower); - m_imgui->bbl_checkbox(_L("Cut to parts"), m_cut_to_parts); - -#if 0 - // Auto segment input - ImGui::PushItemWidth(m_imgui->get_style_scaling() * 150.0); - m_imgui->checkbox(_L("Auto Segment"), m_do_segment); - m_imgui->disabled_begin(!m_do_segment); - ImGui::InputDouble("smoothing_alpha", &m_segment_smoothing_alpha, 0.0f, 0.0f, "%.2f"); - m_segment_smoothing_alpha = std::max(0.1, std::min(100.0, m_segment_smoothing_alpha)); - ImGui::InputInt("segment number", &m_segment_number); - m_segment_number = std::max(1, m_segment_number); - m_imgui->disabled_end(); - - ImGui::Separator(); -#endif - - // Cut button - m_imgui->disabled_begin((!m_keep_upper && !m_keep_lower && !m_cut_to_parts && !m_do_segment)); - const bool cut_clicked = m_imgui->button(_L("Perform cut")); - m_imgui->disabled_end(); - ImGui::SameLine(); - const bool reset_clicked = m_imgui->button(_L("Reset")); - if (reset_clicked) { reset_all(); } GizmoImguiEnd(); ImGuiWrapper::pop_toolbar_style(); +} - // Perform cut - if (cut_clicked && (m_keep_upper || m_keep_lower || m_cut_to_parts || m_do_segment)) - perform_cut(m_parent.get_selection()); +void GLGizmoAdvancedCut::show_tooltip_information(float x, float y) +{ + float caption_max = 0.f; + for (const auto& short_cut : m_shortcuts) { + caption_max = std::max(caption_max, m_imgui->calc_text_size(short_cut.first).x); + } - m_last_active_id = current_active_id; + ImTextureID normal_id = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP); + ImTextureID hover_id = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP_HOVER); + + caption_max += m_imgui->calc_text_size(": ").x + 35.f; + + float font_size = ImGui::GetFontSize(); + ImVec2 button_size = ImVec2(font_size * 1.8, font_size * 1.3); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, {0, ImGui::GetStyle().FramePadding.y}); + ImGui::ImageButton3(normal_id, hover_id, button_size); + + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip2(ImVec2(x, y)); + auto draw_text_with_caption = [this, &caption_max](const wxString &caption, const wxString &text) { + m_imgui->text_colored(ImGuiWrapper::COL_ACTIVE, caption); + ImGui::SameLine(caption_max); + m_imgui->text_colored(ImGuiWrapper::COL_WINDOW_BG, text); + }; + + for (const auto& short_cut : m_shortcuts) + draw_text_with_caption(short_cut.first + ": ", short_cut.second); + ImGui::EndTooltip(); + } + ImGui::PopStyleVar(2); } void GLGizmoAdvancedCut::set_movement(double movement) const @@ -561,6 +629,9 @@ void GLGizmoAdvancedCut::set_movement(double movement) const void GLGizmoAdvancedCut::perform_cut(const Selection& selection) { + if (!can_perform_cut()) + return; + const int instance_idx = selection.get_instance_idx(); const int object_idx = selection.get_object_idx(); @@ -569,17 +640,88 @@ void GLGizmoAdvancedCut::perform_cut(const Selection& selection) // m_cut_z is the distance from the bed. Subtract possible SLA elevation. const GLVolume* first_glvolume = selection.get_volume(*selection.get_volume_idxs().begin()); - // BBS: do segment - if (m_do_segment) + // perform cut { - wxGetApp().plater()->segment(object_idx, instance_idx, m_segment_smoothing_alpha, m_segment_number); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Cut by Plane"); + + ModelObject *mo = wxGetApp().plater()->model().objects[object_idx]; + const bool has_connectors = !mo->cut_connectors.empty(); + + bool create_dowels_as_separate_object = false; + // update connectors pos as offset of its center before cut performing + apply_connectors_in_model(mo, create_dowels_as_separate_object); + + // BBS: do segment + if (m_do_segment) { + wxGetApp().plater()->segment(object_idx, instance_idx, m_segment_smoothing_alpha, m_segment_number); + } else { + wxGetApp().plater()->cut(object_idx, instance_idx, get_plane_points_world_coord(), + only_if(m_keep_upper, ModelObjectCutAttribute::KeepUpper) | + only_if(m_keep_lower, ModelObjectCutAttribute::KeepLower) | + only_if(m_cut_to_parts, ModelObjectCutAttribute::CutToParts) | + only_if(m_place_on_cut_upper, ModelObjectCutAttribute::PlaceOnCutUpper) | + only_if(m_place_on_cut_lower, ModelObjectCutAttribute::PlaceOnCutLower) | + only_if(m_rotate_upper, ModelObjectCutAttribute::FlipUpper) | + only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower) | + only_if(create_dowels_as_separate_object, ModelObjectCutAttribute::CreateDowels) | + only_if(!has_connectors, ModelObjectCutAttribute::InvalidateCutInfo)); + } } - else { - wxGetApp().plater()->cut(object_idx, instance_idx, get_plane_points_world_coord(), - only_if(m_keep_upper, ModelObjectCutAttribute::KeepUpper) | - only_if(m_keep_lower, ModelObjectCutAttribute::KeepLower) | - only_if(m_cut_to_parts, ModelObjectCutAttribute::CutToParts)); +} + +bool GLGizmoAdvancedCut::can_perform_cut() const +{ + if (m_has_invalid_connector || (!m_keep_upper && !m_keep_lower) || m_connectors_editing) + return false; + + return true; + //const auto clipper = m_c->object_clipper(); + //return clipper && clipper->has_valid_contour(); +} + +void GLGizmoAdvancedCut::apply_connectors_in_model(ModelObject *mo, bool &create_dowels_as_separate_object) +{ + clear_selection(); + + for (CutConnector &connector : mo->cut_connectors) { + connector.rotation_m = m_rotate_matrix; + + if (connector.attribs.type == CutConnectorType::Dowel) { + if (connector.attribs.style == CutConnectorStyle::Prizm) connector.height *= 2; + create_dowels_as_separate_object = true; + } else { + // culculate shift of the connector center regarding to the position on the cut plane + Vec3d shifted_center = m_cut_plane_center + Vec3d::UnitZ(); + shifted_center = rotate_vec3d_around_vec3d_with_rotate_matrix(shifted_center, m_cut_plane_center, m_rotate_matrix); + Vec3d norm = (shifted_center - m_cut_plane_center).normalized(); + connector.pos += norm * 0.5 * double(connector.height); + } } + + mo->apply_cut_connectors(_u8L("Connector")); +} + +bool GLGizmoAdvancedCut::is_selection_changed(bool alt_down, bool shift_down) +{ + if (m_hover_id >= c_connectors_group_id) { + if (alt_down) + select_connector(m_hover_id - c_connectors_group_id, false); + else { + if (!shift_down) unselect_all_connectors(); + select_connector(m_hover_id - c_connectors_group_id, true); + } + return true; + } + return false; +} + +void GLGizmoAdvancedCut::select_connector(int idx, bool select) +{ + m_selected[idx] = select; + if (select) + ++m_selected_count; + else + --m_selected_count; } Vec3d GLGizmoAdvancedCut::calc_plane_normal(const std::array& plane_points) const @@ -630,5 +772,1091 @@ void GLGizmoAdvancedCut::finish_rotation() update_plane_points(); } + +void GLGizmoAdvancedCut::put_connectors_on_cut_plane(const Vec3d &cp_normal, double cp_offset) +{ + ModelObject *mo = m_c->selection_info()->model_object(); + if (CutConnectors &connectors = mo->cut_connectors; !connectors.empty()) { + const float sla_shift = m_c->selection_info()->get_sla_shift(); + const Vec3d &instance_offset = mo->instances[m_c->selection_info()->get_active_instance()]->get_offset(); + + for (auto &connector : connectors) { + // convert connetor pos to the world coordinates + Vec3d pos = connector.pos + instance_offset; + pos[Z] += sla_shift; + // scalar distance from point to plane along the normal + double distance = -cp_normal.dot(pos) + cp_offset; + // move connector + connector.pos += distance * cp_normal; + } + } +} + +void GLGizmoAdvancedCut::update_clipper() +{ + BoundingBoxf3 box = bounding_box(); + double radius = box.radius(); + Vec3d plane_center = m_cut_plane_center; + + Vec3d begin, end = begin = plane_center; + begin[Z] = box.center().z() - radius; + end[Z] = box.center().z() + radius; + + double phi; + Vec3d rotation_axis; + Matrix3d rotation_matrix; + Geometry::rotation_from_two_vectors(Vec3d::UnitZ(), m_cut_plane_normal, rotation_axis, phi, &rotation_matrix); + + m_rotate_matrix.setIdentity(); + m_rotate_matrix = rotation_matrix * m_rotate_matrix; + + begin = rotate_vec3d_around_vec3d_with_rotate_matrix(begin, plane_center, m_rotate_matrix); + end = rotate_vec3d_around_vec3d_with_rotate_matrix(end, plane_center, m_rotate_matrix); + + Vec3d normal = end - begin; + + if (!is_looking_forward()) { + end = begin = plane_center; + begin[Z] = box.center().z() + radius; + end[Z] = box.center().z() - radius; + + begin = rotate_vec3d_around_vec3d_with_rotate_matrix(begin, plane_center, m_rotate_matrix); + end = rotate_vec3d_around_vec3d_with_rotate_matrix(end, plane_center, m_rotate_matrix); + + // recalculate normal for clipping plane, if camera is looking downward to cut plane + normal = end - begin; + if (normal == Vec3d::Zero()) + return; + } + + // calculate normal and offset for clipping plane + double dist = (plane_center - begin).norm(); + dist = std::clamp(dist, 0.0001, normal.norm()); + normal.normalize(); + const double offset = normal.dot(begin) + dist; + + m_c->object_clipper()->set_range_and_pos(normal, offset, dist); + + put_connectors_on_cut_plane(normal, offset); +} + +void GLGizmoAdvancedCut::render_cut_plane_and_grabbers() +{ + const Selection & selection = m_parent.get_selection(); + const BoundingBoxf3 &box = selection.get_bounding_box(); + // box center is the coord of object in the world coordinate + Vec3d object_offset = box.center(); + // plane points is in object coordinate + Vec3d plane_center = get_plane_center(); + + m_cut_plane_center = object_offset + plane_center; + + // rotate plane + std::array plane_points_rot; + { + for (int i = 0; i < plane_points_rot.size(); i++) { + plane_points_rot[i] = m_cut_plane_points[i] - plane_center; + } + + if (m_rotation(0) > EPSILON) + rotate_x_3d(plane_points_rot, m_rotation(0)); + if (m_rotation(1) > EPSILON) + rotate_y_3d(plane_points_rot, m_rotation(1)); + if (m_rotation(2) > EPSILON) + rotate_z_3d(plane_points_rot, m_rotation(2)); + + for (int i = 0; i < plane_points_rot.size(); i++) { + plane_points_rot[i] += plane_center; + } + } + + // move plane + Vec3d plane_normal_rot = calc_plane_normal(plane_points_rot); + m_cut_plane_normal = plane_normal_rot; + for (int i = 0; i < plane_points_rot.size(); i++) { + plane_points_rot[i] += plane_normal_rot * m_movement; + } + + // transfer from object coordindate to the world coordinate + for (Vec3d& point : plane_points_rot) { + point += object_offset; + } + + // draw plane + glsafe(::glEnable(GL_DEPTH_TEST)); + glsafe(::glDisable(GL_CULL_FACE)); + glsafe(::glEnable(GL_BLEND)); + glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + + ::glBegin(GL_QUADS); + ::glColor4f(0.8f, 0.8f, 0.8f, 0.5f); + for (const Vec3d& point : plane_points_rot) { + ::glVertex3f(point(0), point(1), point(2)); + } + glsafe(::glEnd()); + + glsafe(::glEnable(GL_CULL_FACE)); + glsafe(::glDisable(GL_BLEND)); + + // Draw the grabber and the connecting line + Vec3d plane_center_rot = calc_plane_center(plane_points_rot); + m_move_grabber.center = plane_center_rot + plane_normal_rot * Offset; + // m_move_grabber.angles = m_current_base_rotation + m_rotation; + + glsafe(::glDisable(GL_DEPTH_TEST)); + glsafe(::glLineWidth(m_hover_id != -1 ? 2.0f : 1.5f)); + glsafe(::glColor3f(1.0, 1.0, 0.0)); + glLineStipple(1, 0x0FFF); + glEnable(GL_LINE_STIPPLE); + ::glBegin(GL_LINES); + ::glVertex3dv(plane_center_rot.data()); + ::glVertex3dv(m_move_grabber.center.data()); + glsafe(::glEnd()); + glDisable(GL_LINE_STIPPLE); + + // std::copy(std::begin(GrabberColor), std::end(GrabberColor), m_move_grabber.color); + // m_move_grabber.color = GrabberColor; + // m_move_grabber.hover_color = GrabberHoverColor; + // m_move_grabber.render(m_hover_id == get_group_id(), (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0)); + bool hover = (m_hover_id == get_group_id()); + std::array render_color; + if (hover) { + render_color = GrabberHoverColor; + } + else + render_color = GrabberColor; + + const GLModel &cube = m_move_grabber.get_cube(); + // BBS set to fixed size grabber + // float fullsize = 2 * (dragging ? get_dragging_half_size(size) : get_half_size(size)); + float fullsize = 8.0f; + if (GLGizmoBase::INV_ZOOM > 0) { + fullsize = m_move_grabber.FixedGrabberSize * GLGizmoBase::INV_ZOOM; + } + + const_cast(&cube)->set_color(-1, render_color); + + glsafe(::glPushMatrix()); + glsafe(::glTranslated(m_move_grabber.center.x(), m_move_grabber.center.y(), m_move_grabber.center.z())); + glsafe(::glMultMatrixd(m_rotate_matrix.data())); + + glsafe(::glScaled(fullsize, fullsize, fullsize)); + cube.render(); + glsafe(::glPopMatrix()); + + // Should be placed at last, because GLGizmoRotate3D clears depth buffer + set_center(m_cut_plane_center); + GLGizmoRotate3D::on_render(); +} + +void GLGizmoAdvancedCut::render_connectors() +{ + ::glEnable(GL_DEPTH_TEST); + + const ModelObject *mo = m_c->selection_info()->model_object(); + auto inst_id = m_c->selection_info()->get_active_instance(); + if (inst_id < 0) + return; + + const CutConnectors &connectors = mo->cut_connectors; + if (connectors.size() != m_selected.size()) { + clear_selection(); + m_selected.resize(connectors.size(), false); + } + + const ModelInstance *mi = mo->instances[inst_id]; + const Vec3d & instance_offset = mi->get_offset(); + const double sla_shift = double(m_c->selection_info()->get_sla_shift()); + + m_has_invalid_connector = false; + m_info_stats.invalidate(); + + ColorRGBA render_color = CONNECTOR_DEF_COLOR; + for (size_t i = 0; i < connectors.size(); ++i) { + const CutConnector &connector = connectors[i]; + + float height = connector.height; + // recalculate connector position to world position + Vec3d pos = connector.pos + instance_offset + sla_shift * Vec3d::UnitZ(); + + // First decide about the color of the point. + const bool conflict_connector = is_conflict_for_connector(i, connectors, pos); + if (conflict_connector) { + m_has_invalid_connector = true; + render_color = CONNECTOR_ERR_COLOR; + } else // default connector color + render_color = connector.attribs.type == CutConnectorType::Dowel ? DOWEL_COLOR : PLAG_COLOR; + + if (!m_connectors_editing) + render_color = CONNECTOR_ERR_COLOR; + else if (size_t(m_hover_id - 4) == i) + render_color = conflict_connector ? HOVERED_ERR_COLOR : connector.attribs.type == CutConnectorType::Dowel ? HOVERED_DOWEL_COLOR : HOVERED_PLAG_COLOR; + else if (m_selected[i]) + render_color = connector.attribs.type == CutConnectorType::Dowel ? SELECTED_DOWEL_COLOR : SELECTED_PLAG_COLOR; + + const Camera &camera = wxGetApp().plater()->get_camera(); + if (connector.attribs.type == CutConnectorType::Dowel && connector.attribs.style == CutConnectorStyle::Prizm) { + pos -= height * m_cut_plane_normal; + height *= 2; + } else if (!is_looking_forward()) + pos -= 0.05 * m_cut_plane_normal; + + Transform3d translate_tf = Transform3d::Identity(); + translate_tf.translate(pos); + + Transform3d scale_tf = Transform3d::Identity(); + scale_tf.scale(Vec3f(connector.radius, connector.radius, height).cast()); + + const Transform3d view_model_matrix = translate_tf * m_rotate_matrix * scale_tf; + + render_connector_model(m_shapes[connector.attribs], render_color, view_model_matrix); + } +} + +void GLGizmoAdvancedCut::render_clipper_cut() +{ + glsafe(::glEnable(GL_DEPTH_TEST)); + m_c->object_clipper()->render_cut(); + glsafe(::glDisable(GL_DEPTH_TEST)); +} + +void GLGizmoAdvancedCut::render_cut_line() +{ + if (!cut_line_processing() || m_cut_line_end == Vec3d::Zero()) + return; + + glsafe(::glEnable(GL_DEPTH_TEST)); + glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); + glsafe(::glColor3f(0.0, 1.0, 0.0)); + glEnable(GL_LINE_STIPPLE); + ::glBegin(GL_LINES); + ::glVertex3dv(m_cut_line_begin.data()); + ::glVertex3dv(m_cut_line_end.data()); + glsafe(::glEnd()); + glDisable(GL_LINE_STIPPLE); +} + +void GLGizmoAdvancedCut::render_connector_model(GLModel &model, const std::array &color, Transform3d view_model_matrix, bool for_picking) +{ + glPushMatrix(); + GLShaderProgram *shader = nullptr; + if (for_picking) + shader = wxGetApp().get_shader("cali"); + else + shader = wxGetApp().get_shader("gouraud_light"); + if (shader) { + shader->start_using(); + + glsafe(::glMultMatrixd(view_model_matrix.data())); + + model.set_color(-1, color); + model.render(); + + shader->stop_using(); + } + glPopMatrix(); +} + +void GLGizmoAdvancedCut::clear_selection() +{ + m_selected.clear(); + m_selected_count = 0; +} + +void GLGizmoAdvancedCut::init_connector_shapes() +{ + for (const CutConnectorType &type : {CutConnectorType::Dowel, CutConnectorType::Plug}) + for (const CutConnectorStyle &style : {CutConnectorStyle::Frustum, CutConnectorStyle::Prizm}) + for (const CutConnectorShape &shape : {CutConnectorShape::Circle, CutConnectorShape::Hexagon, CutConnectorShape::Square, CutConnectorShape::Triangle}) { + const CutConnectorAttributes attribs = {type, style, shape}; + const indexed_triangle_set its = ModelObject::get_connector_mesh(attribs); + m_shapes[attribs].init_from(its); + } +} + +void GLGizmoAdvancedCut::set_connectors_editing(bool connectors_editing) +{ + m_connectors_editing = connectors_editing; + // todo: zhimin need a better method + // after change render mode, need update for scene + on_render(); +} + +void GLGizmoAdvancedCut::reset_connectors() +{ + m_c->selection_info()->model_object()->cut_connectors.clear(); + clear_selection(); +} + +void GLGizmoAdvancedCut::update_connector_shape() +{ + CutConnectorAttributes attribs = {m_connector_type, CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id)}; + + const indexed_triangle_set its = ModelObject::get_connector_mesh(attribs); + m_connector_mesh.clear(); + m_connector_mesh = TriangleMesh(its); +} + +void GLGizmoAdvancedCut::apply_selected_connectors(std::function apply_fn) +{ + for (size_t idx = 0; idx < m_selected.size(); idx++) + if (m_selected[idx]) + apply_fn(idx); +} + +void GLGizmoAdvancedCut::select_all_connectors() +{ + std::fill(m_selected.begin(), m_selected.end(), true); + m_selected_count = int(m_selected.size()); +} + +void GLGizmoAdvancedCut::unselect_all_connectors() +{ + std::fill(m_selected.begin(), m_selected.end(), false); + m_selected_count = 0; + validate_connector_settings(); +} + +void GLGizmoAdvancedCut::validate_connector_settings() +{ + if (m_connector_depth_ratio < 0.f) + m_connector_depth_ratio = 3.f; + if (m_connector_depth_ratio_tolerance < 0.f) + m_connector_depth_ratio_tolerance = 0.1f; + if (m_connector_size < 0.f) + m_connector_size = 2.5f; + if (m_connector_size_tolerance < 0.f) + m_connector_size_tolerance = 0.f; + + if (m_connector_type == CutConnectorType::Undef) + m_connector_type = CutConnectorType::Plug; + if (m_connector_style == size_t(CutConnectorStyle::Undef)) + m_connector_style = size_t(CutConnectorStyle::Prizm); + if (m_connector_shape_id == size_t(CutConnectorShape::Undef)) + m_connector_shape_id = size_t(CutConnectorShape::Circle); +} + +bool GLGizmoAdvancedCut::add_connector(CutConnectors &connectors, const Vec2d &mouse_position) +{ + if (!m_connectors_editing) + return false; + + Vec3d pos; + Vec3d pos_world; + if (unproject_on_cut_plane(mouse_position.cast(), pos, pos_world)) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Add connector"); + unselect_all_connectors(); + + connectors.emplace_back(pos, m_rotate_matrix, m_connector_size * 0.5f, m_connector_depth_ratio, m_connector_size_tolerance, m_connector_depth_ratio_tolerance, + CutConnectorAttributes(CutConnectorType(m_connector_type), CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id))); + m_selected.push_back(true); + m_selected_count = 1; + assert(m_selected.size() == connectors.size()); + m_parent.set_as_dirty(); + + return true; + } + return false; +} + +bool GLGizmoAdvancedCut::delete_selected_connectors() +{ + CutConnectors &connectors = m_c->selection_info()->model_object()->cut_connectors; + if (connectors.empty()) + return false; + + Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Delete connector"); + + // remove connectors + for (int i = int(connectors.size()) - 1; i >= 0; i--) + if (m_selected[i]) connectors.erase(connectors.begin() + i); + // remove selections + m_selected.erase(std::remove_if(m_selected.begin(), m_selected.end(), + [](const auto &selected) { + return selected;}), + m_selected.end()); + + m_selected_count = 0; + + assert(m_selected.size() == connectors.size()); + m_parent.set_as_dirty(); + return true; +} + +bool GLGizmoAdvancedCut::is_outside_of_cut_contour(size_t idx, const CutConnectors &connectors, const Vec3d cur_pos) +{ + // check if connector pos is out of clipping plane + if (m_c->object_clipper() && !m_c->object_clipper()->is_projection_inside_cut(cur_pos)) { + m_info_stats.outside_cut_contour++; + return true; + } + + // check if connector bottom contour is out of clipping plane + const CutConnector & cur_connector = connectors[idx]; + const CutConnectorShape shape = CutConnectorShape(cur_connector.attribs.shape); + const int sectorCount = shape == CutConnectorShape::Triangle ? 3 : + shape == CutConnectorShape::Square ? 4 : + shape == CutConnectorShape::Circle ? 60: // supposably, 60 points are enough for conflict detection + shape == CutConnectorShape::Hexagon ? 6 : 1 ; + + indexed_triangle_set mesh; + auto & vertices = mesh.vertices; + vertices.reserve(sectorCount + 1); + + float fa = 2 * PI / sectorCount; + auto vec = Eigen::Vector2f(0, cur_connector.radius); + for (float angle = 0; angle < 2.f * PI; angle += fa) { + Vec2f p = Eigen::Rotation2Df(angle) * vec; + vertices.emplace_back(Vec3f(p(0), p(1), 0.f)); + } + + Transform3d transform = Transform3d::Identity(); + transform.translate(cur_pos); + its_transform(mesh, transform * m_rotate_matrix); + + for (auto vertex : vertices) { + if (m_c->object_clipper() && !m_c->object_clipper()->is_projection_inside_cut(vertex.cast())) { + m_info_stats.outside_cut_contour++; + return true; + } + } + + return false; +} + +bool GLGizmoAdvancedCut::is_conflict_for_connector(size_t idx, const CutConnectors &connectors, const Vec3d cur_pos) +{ + if (is_outside_of_cut_contour(idx, connectors, cur_pos)) + return true; + + const CutConnector &cur_connector = connectors[idx]; + + Transform3d translate_tf = Transform3d::Identity(); + translate_tf.translate(cur_pos); + Transform3d scale_tf = Transform3d::Identity(); + scale_tf.scale(Vec3f(cur_connector.radius, cur_connector.radius, cur_connector.height).cast()); + const Transform3d matrix = translate_tf * m_rotate_matrix * scale_tf; + + const BoundingBoxf3 cur_tbb = m_shapes[cur_connector.attribs].get_bounding_box().transformed(matrix); + + // check if connector's bounding box is inside the object's bounding box + if (!bounding_box().contains(cur_tbb)) { + m_info_stats.outside_bb++; + return true; + } + + // check if connectors are overlapping + for (size_t i = 0; i < connectors.size(); ++i) { + if (i == idx) continue; + const CutConnector &connector = connectors[i]; + + if ((connector.pos - cur_connector.pos).norm() < double(connector.radius + cur_connector.radius)) { + m_info_stats.is_overlap = true; + return true; + } + } + + return false; +} + +void GLGizmoAdvancedCut::check_conflict_for_all_connectors() +{ + const ModelObject *mo = m_c->selection_info()->model_object(); + auto inst_id = m_c->selection_info()->get_active_instance(); + if (inst_id < 0) + return; + + const CutConnectors &connectors = mo->cut_connectors; + const ModelInstance *mi = mo->instances[inst_id]; + const Vec3d & instance_offset = mi->get_offset(); + const double sla_shift = double(m_c->selection_info()->get_sla_shift()); + + m_has_invalid_connector = false; + m_info_stats.invalidate(); + + for (size_t i = 0; i < connectors.size(); ++i) { + const CutConnector &connector = connectors[i]; + + Vec3d pos = connector.pos + instance_offset + sla_shift * Vec3d::UnitZ(); + + // First decide about the color of the point. + const bool conflict_connector = is_conflict_for_connector(i, connectors, pos); + if (conflict_connector) { + m_has_invalid_connector = true; + } + } +} + +void GLGizmoAdvancedCut::render_cut_plane_input_window(float x, float y, float bottom_limit) +{ + // float unit_size = m_imgui->get_style_scaling() * 48.0f; + float space_size = m_imgui->get_style_scaling() * 8; + float movement_cap = m_imgui->calc_text_size(_L("Movement:")).x; + float rotate_cap = m_imgui->calc_text_size(_L("Rotate")).x; + float caption_size = std::max(movement_cap, rotate_cap) + 2 * space_size; + bool imperial_units = wxGetApp().app_config->get("use_inches") == "1"; + unsigned int current_active_id = ImGui::GetActiveID(); + + Vec3d rotation = {Geometry::rad2deg(m_rotation(0)), Geometry::rad2deg(m_rotation(1)), Geometry::rad2deg(m_rotation(2))}; + char buf[3][64]; + float buf_size[3]; + float vec_max = 0, unit_size = 0; + for (int i = 0; i < 3; i++) { + ImGui::DataTypeFormatString(buf[i], IM_ARRAYSIZE(buf[i]), ImGuiDataType_Double, (void *) &rotation[i], "%.2f"); + buf_size[i] = ImGui::CalcTextSize(buf[i]).x; + vec_max = std::max(buf_size[i], vec_max); + } + + float buf_size_max = ImGui::CalcTextSize("-100.00").x; + if (vec_max < buf_size_max) { + unit_size = buf_size_max + ImGui::GetStyle().FramePadding.x * 2.0f; + } else { + unit_size = vec_max + ImGui::GetStyle().FramePadding.x * 2.0f; + } + + ImGui::PushItemWidth(caption_size); + ImGui::Dummy(ImVec2(caption_size, -1)); + ImGui::SameLine(caption_size + 1 * space_size); + ImGui::PushItemWidth(unit_size); + ImGui::TextAlignCenter("X"); + ImGui::SameLine(caption_size + 1 * unit_size + 2 * space_size); + ImGui::PushItemWidth(unit_size); + ImGui::TextAlignCenter("Y"); + ImGui::SameLine(caption_size + 2 * unit_size + 3 * space_size); + ImGui::PushItemWidth(unit_size); + ImGui::TextAlignCenter("Z"); + + ImGui::AlignTextToFramePadding(); + + // Rotation input box + ImGui::PushItemWidth(caption_size); + m_imgui->text(_L("Rotation") + " "); + ImGui::SameLine(caption_size + 1 * space_size); + ImGui::PushItemWidth(unit_size); + ImGui::BBLInputDouble("##cut_rotation_x", &rotation[0], 0.0f, 0.0f, "%.2f"); + ImGui::SameLine(caption_size + 1 * unit_size + 2 * space_size); + ImGui::PushItemWidth(unit_size); + ImGui::BBLInputDouble("##cut_rotation_y", &rotation[1], 0.0f, 0.0f, "%.2f"); + ImGui::SameLine(caption_size + 2 * unit_size + 3 * space_size); + ImGui::PushItemWidth(unit_size); + ImGui::BBLInputDouble("##cut_rotation_z", &rotation[2], 0.0f, 0.0f, "%.2f"); + if (current_active_id != m_last_active_id) { + if (std::abs(Geometry::rad2deg(m_rotation(0)) - m_buffered_rotation(0)) > EPSILON || std::abs(Geometry::rad2deg(m_rotation(1)) - m_buffered_rotation(1)) > EPSILON || + std::abs(Geometry::rad2deg(m_rotation(2)) - m_buffered_rotation(2)) > EPSILON) { + m_rotation = m_buffered_rotation; + m_buffered_rotation.setZero(); + update_plane_points(); + m_parent.post_event(SimpleEvent(wxEVT_PAINT)); + } + } else { + m_buffered_rotation(0) = Geometry::deg2rad(rotation(0)); + m_buffered_rotation(1) = Geometry::deg2rad(rotation(1)); + m_buffered_rotation(2) = Geometry::deg2rad(rotation(2)); + } + + ImGui::Separator(); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4.0f, 10.0f)); + // Movement input box + double movement = m_movement; + ImGui::PushItemWidth(caption_size); + ImGui::AlignTextToFramePadding(); + m_imgui->text(_L("Movement") + " "); + ImGui::SameLine(caption_size + 1 * space_size); + ImGui::PushItemWidth(3 * unit_size + 2 * space_size); + ImGui::BBLInputDouble("##cut_movement", &movement, 0.0f, 0.0f, "%.2f"); + if (current_active_id != m_last_active_id) { + if (std::abs(m_buffered_movement - m_movement) > EPSILON) { + m_movement = m_buffered_movement; + m_buffered_movement = 0.0; + + // update absolute height + Vec3d plane_normal = get_plane_normal(); + m_height_delta = plane_normal(2) * m_movement; + m_height += m_height_delta; + m_buffered_height = m_height; + + update_plane_points(); + m_parent.post_event(SimpleEvent(wxEVT_PAINT)); + } + } else { + m_buffered_movement = movement; + } + + // height input box + double height = m_height; + ImGui::PushItemWidth(caption_size); + ImGui::AlignTextToFramePadding(); + // only allow setting height when cut plane is horizontal + Vec3d plane_normal = get_plane_normal(); + m_imgui->disabled_begin(std::abs(plane_normal(0)) > EPSILON || std::abs(plane_normal(1)) > EPSILON); + m_imgui->text(_L("Height") + " "); + ImGui::SameLine(caption_size + 1 * space_size); + ImGui::PushItemWidth(3 * unit_size + 2 * space_size); + ImGui::BBLInputDouble("##cut_height", &height, 0.0f, 0.0f, "%.2f"); + if (current_active_id != m_last_active_id) { + if (std::abs(m_buffered_height - m_height) > EPSILON) { + m_height_delta = m_buffered_height - m_height; + m_height = m_buffered_height; + update_plane_points(); + m_parent.post_event(SimpleEvent(wxEVT_PAINT)); + } + } else { + m_buffered_height = height; + } + ImGui::PopStyleVar(1); + m_imgui->disabled_end(); + + CutConnectors &connectors = m_c->selection_info()->model_object()->cut_connectors; + const bool has_connectors = !connectors.empty(); + + m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower || m_cut_to_parts); + if (m_imgui->button(has_connectors ? _L("Edit connectors") : _L("Add connectors"))) set_connectors_editing(true); + m_imgui->disabled_end(); + + ImGui::Separator(); + + float label_width = 0; + for (const wxString& label : {_L("Upper part"), _L("Lower part")}) { + const float width = m_imgui->calc_text_size(label).x + m_imgui->scaled(1.5f); + if (label_width < width) + label_width = width; + } + + auto render_part_action_line = [this, label_width, &connectors](const wxString &label, const wxString &suffix, bool &keep_part, bool &place_on_cut_part, bool &rotate_part) { + bool keep = true; + ImGui::AlignTextToFramePadding(); + m_imgui->text(label); + + ImGui::SameLine(label_width); + + m_imgui->disabled_begin(!connectors.empty() || m_cut_to_parts); + m_imgui->bbl_checkbox(_L("Keep") + suffix, connectors.empty() ? keep_part : keep); + m_imgui->disabled_end(); + + ImGui::SameLine(); + + m_imgui->disabled_begin(!keep_part || m_cut_to_parts); + if (m_imgui->bbl_checkbox(_L("Place on cut") + suffix, place_on_cut_part)) + rotate_part = false; + ImGui::SameLine(); + if (m_imgui->bbl_checkbox(_L("Flip") + suffix, rotate_part)) + place_on_cut_part = false; + m_imgui->disabled_end(); + }; + + m_imgui->text(_L("After cut") + ": "); + render_part_action_line( _L("Upper part"), "##upper", m_keep_upper, m_place_on_cut_upper, m_rotate_upper); + render_part_action_line( _L("Lower part"), "##lower", m_keep_lower, m_place_on_cut_lower, m_rotate_lower); + + m_imgui->disabled_begin(has_connectors); + m_imgui->bbl_checkbox(_L("Cut to parts"), m_cut_to_parts); + m_imgui->disabled_end(); + +#if 0 + // Auto segment input + ImGui::PushItemWidth(m_imgui->get_style_scaling() * 150.0); + m_imgui->checkbox(_L("Auto Segment"), m_do_segment); + m_imgui->disabled_begin(!m_do_segment); + ImGui::InputDouble("smoothing_alpha", &m_segment_smoothing_alpha, 0.0f, 0.0f, "%.2f"); + m_segment_smoothing_alpha = std::max(0.1, std::min(100.0, m_segment_smoothing_alpha)); + ImGui::InputInt("segment number", &m_segment_number); + m_segment_number = std::max(1, m_segment_number); + m_imgui->disabled_end(); + + ImGui::Separator(); +#endif + + // Cut button + m_imgui->disabled_begin(!can_perform_cut()); + if (m_imgui->button(_L("Perform cut"))) + perform_cut(m_parent.get_selection()); + m_imgui->disabled_end(); + ImGui::SameLine(); + const bool reset_clicked = m_imgui->button(_L("Reset")); + if (reset_clicked) { reset_all(); } + + m_last_active_id = current_active_id; +} + +void GLGizmoAdvancedCut::init_connectors_input_window_data() +{ + CutConnectors &connectors = m_c->selection_info()->model_object()->cut_connectors; + + float connectors_cap = m_imgui->calc_text_size(_L("Connectors")).x; + float type_cap = m_imgui->calc_text_size(_L("Type")).x; + float style_cap = m_imgui->calc_text_size(_L("Style")).x; + float shape_cap = m_imgui->calc_text_size(_L("Shape")).x; + float depth_ratio_cap = m_imgui->calc_text_size(_L("Depth ratio")).x; + float size_cap = m_imgui->calc_text_size(_L("Size")).x; + float max_lable_size = std::max(std::max(std::max(connectors_cap, type_cap), std::max(style_cap, shape_cap)), std::max(depth_ratio_cap, size_cap)); + + m_label_width = double(max_lable_size + 3 + ImGui::GetStyle().WindowPadding.x); + m_control_width = m_imgui->get_font_size() * 9.f; + + m_editing_window_width = 1.45 * m_control_width + 11; + + if (m_connectors_editing && m_selected_count > 0) { + float depth_ratio {UndefFloat}; + float depth_ratio_tolerance {UndefFloat}; + float radius {UndefFloat}; + float radius_tolerance {UndefFloat}; + CutConnectorType type{CutConnectorType::Undef}; + CutConnectorStyle style{CutConnectorStyle::Undef}; + CutConnectorShape shape{CutConnectorShape::Undef}; + + bool is_init = false; + for (size_t idx = 0; idx < m_selected.size(); idx++) + if (m_selected[idx]) { + const CutConnector &connector = connectors[idx]; + if (!is_init) { + depth_ratio = connector.height; + depth_ratio_tolerance = connector.height_tolerance; + radius = connector.radius; + radius_tolerance = connector.radius_tolerance; + type = connector.attribs.type; + style = connector.attribs.style; + shape = connector.attribs.shape; + + if (m_selected_count == 1) break; + is_init = true; + } else { + if (!is_approx(depth_ratio, connector.height)) + depth_ratio = UndefFloat; + if (!is_approx(depth_ratio_tolerance, connector.height_tolerance)) + depth_ratio_tolerance = UndefFloat; + if (!is_approx(radius, connector.radius)) + radius = UndefFloat; + if (!is_approx(radius_tolerance, connector.radius_tolerance)) + radius_tolerance = UndefFloat; + + if (type != connector.attribs.type) + type = CutConnectorType::Undef; + if (style != connector.attribs.style) + style = CutConnectorStyle::Undef; + if (shape != connector.attribs.shape) + shape = CutConnectorShape::Undef; + } + } + + m_connector_depth_ratio = depth_ratio; + m_connector_depth_ratio_tolerance = depth_ratio_tolerance; + m_connector_size = 2.f * radius; + m_connector_size_tolerance = radius_tolerance; + m_connector_type = type; + m_connector_style = size_t(style); + m_connector_shape_id = size_t(shape); + } +} + +void GLGizmoAdvancedCut::render_connectors_input_window(float x, float y, float bottom_limit) +{ + CutConnectors &connectors = m_c->selection_info()->model_object()->cut_connectors; + + // update when change input window + m_imgui->set_requires_extra_frame(); + + ImGui::AlignTextToFramePadding(); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Connectors")); + + m_imgui->disabled_begin(connectors.empty()); + ImGui::SameLine(m_label_width); + if (render_reset_button("connectors", _u8L("Remove connectors"))) + reset_connectors(); + m_imgui->disabled_end(); + + m_imgui->text(_L("Type")); + ImGui::PushStyleColor(ImGuiCol_CheckMark, ImVec4(0.00f, 0.00f, 0.00f, 1.00f)); + bool type_changed = render_connect_type_radio_button(CutConnectorType::Plug); + type_changed |= render_connect_type_radio_button(CutConnectorType::Dowel); + if (type_changed) + apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.type = CutConnectorType(m_connector_type); }); + ImGui::PopStyleColor(1); + + std::vector connector_styles = {_u8L("Prizm"), _u8L("Frustum")}; + std::vector connector_shapes = { _u8L("Triangle"), _u8L("Square"), _u8L("Hexagon"), _u8L("Circle") }; + + m_imgui->disabled_begin(m_connector_type == CutConnectorType::Dowel); + if (type_changed && m_connector_type == CutConnectorType::Dowel) { + m_connector_style = size_t(CutConnectorStyle::Prizm); + apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.style = CutConnectorStyle(m_connector_style); }); + } + + ImGuiWrapper::push_combo_style(m_parent.get_scale()); + if (render_combo(_u8L("Style"), connector_styles, m_connector_style)) + apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.style = CutConnectorStyle(m_connector_style); }); + m_imgui->disabled_end(); + + if (render_combo(_u8L("Shape"), connector_shapes, m_connector_shape_id)) + apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); }); + ImGuiWrapper::pop_combo_style(); + + if (render_slider_double_input(_u8L("Depth ratio"), m_connector_depth_ratio, m_connector_depth_ratio_tolerance)) + apply_selected_connectors([this, &connectors](size_t idx) { + if (m_connector_depth_ratio > 0) + connectors[idx].height = m_connector_depth_ratio; + if (m_connector_depth_ratio_tolerance >= 0) + connectors[idx].height_tolerance = m_connector_depth_ratio_tolerance; + }); + + if (render_slider_double_input(_u8L("Size"), m_connector_size, m_connector_size_tolerance)) + apply_selected_connectors([this, &connectors](size_t idx) { + if (m_connector_size > 0) + connectors[idx].radius = 0.5f * m_connector_size; + if (m_connector_size_tolerance >= 0) + connectors[idx].radius_tolerance = m_connector_size_tolerance; + }); + + ImGui::Separator(); + + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0f, 10.0f)); + float get_cur_y = ImGui::GetContentRegionMax().y + ImGui::GetFrameHeight() + y; + show_tooltip_information(x, get_cur_y); + + float f_scale = m_parent.get_gizmos_manager().get_layout_scale(); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale)); + + ImGui::SameLine(); + if (m_imgui->button(_L("Confirm connectors"))) { + unselect_all_connectors(); + set_connectors_editing(false); + } + + ImGui::SameLine(m_label_width + m_editing_window_width - m_imgui->calc_text_size(_L("Cancel")).x - m_imgui->get_style_scaling() * 8); + + if (m_imgui->button(_L("Cancel"))) { + reset_connectors(); + set_connectors_editing(false); + } + + ImGui::PopStyleVar(2); +} + +void GLGizmoAdvancedCut::render_input_window_warning() const +{ + if (m_has_invalid_connector) { + wxString out = /*wxString(ImGui::WarningMarkerSmall)*/ _L("Warning") + ": " + _L("Invalid connectors detected") + ":"; + if (m_info_stats.outside_cut_contour > size_t(0)) + out += "\n - " + std::to_string(m_info_stats.outside_cut_contour) + + (m_info_stats.outside_cut_contour == 1 ? _L("connector is out of cut contour") : _L("connectors are out of cut contour")); + if (m_info_stats.outside_bb > size_t(0)) + out += "\n - " + std::to_string(m_info_stats.outside_bb) + + (m_info_stats.outside_bb == 1 ? _L("connector is out of object") : _L("connectors is out of object")); + if (m_info_stats.is_overlap) + out += "\n - " + _L("Some connectors are overlapped"); + m_imgui->text(out); + } + if (!m_keep_upper && !m_keep_lower) m_imgui->text(/*wxString(ImGui::WarningMarkerSmall)*/_L("Warning") + ": " + _L("Invalid state. \nNo one part is selected for keep after cut")); +} + +bool GLGizmoAdvancedCut::render_reset_button(const std::string &label_id, const std::string &tooltip) const +{ + const ImGuiStyle &style = ImGui::GetStyle(); + + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, {1, style.ItemSpacing.y}); + + ImGui::PushStyleColor(ImGuiCol_Button, {0.25f, 0.25f, 0.25f, 0.0f}); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, {0.4f, 0.4f, 0.4f, 1.0f}); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, {0.4f, 0.4f, 0.4f, 1.0f}); + + bool revert = m_imgui->button(wxString(ImGui::RevertBtn)); + + ImGui::PopStyleColor(3); + + if (ImGui::IsItemHovered()) + m_imgui->tooltip(tooltip.c_str(), ImGui::GetFontSize() * 20.0f); + + ImGui::PopStyleVar(); + + return revert; +} + +bool GLGizmoAdvancedCut::render_connect_type_radio_button(CutConnectorType type) +{ + ImGui::SameLine(type == CutConnectorType::Plug ? m_label_width : 2 * m_label_width); + ImGui::PushItemWidth(m_control_width); + + wxString radio_name; + switch (type) { + case CutConnectorType::Plug: + radio_name = _L("Plug"); + break; + case CutConnectorType::Dowel: + radio_name = _L("Dowel"); + break; + default: + break; + } + + if (m_imgui->radio_button(radio_name, m_connector_type == type)) { + m_connector_type = type; + update_connector_shape(); + return true; + } + return false; +} + +bool GLGizmoAdvancedCut::render_combo(const std::string &label, const std::vector &lines, size_t &selection_idx) +{ + ImGui::AlignTextToFramePadding(); + m_imgui->text(label); + ImGui::SameLine(m_label_width); + ImGui::PushItemWidth(m_editing_window_width); + + size_t selection_out = selection_idx; + + if (ImGui::BBLBeginCombo(("##" + label).c_str(), lines[selection_idx].c_str(), 0)) { + for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) { + ImGui::PushID(int(line_idx)); + if (ImGui::Selectable("", line_idx == selection_idx)) + selection_out = line_idx; + + ImGui::SameLine(); + ImGui::Text("%s", lines[line_idx].c_str()); + ImGui::PopID(); + } + + ImGui::EndCombo(); + } + + bool is_changed = selection_idx != selection_out; + selection_idx = selection_out; + + if (is_changed) update_connector_shape(); + + return is_changed; +} + +bool GLGizmoAdvancedCut::render_slider_double_input(const std::string &label, float &value_in, float &tolerance_in) +{ + double slider_with = 0.24 * m_editing_window_width; // m_control_width * 0.35; + double item_in_gap = 0.01 * m_editing_window_width; + double item_out_gap = 0.02 * m_editing_window_width; + double first_input_width = 0.33 * m_editing_window_width; + double second_input_width = 0.15 * m_editing_window_width; + + ImGui::AlignTextToFramePadding(); + m_imgui->text(label); + ImGui::SameLine(m_label_width); + ImGui::PushItemWidth(slider_with); + + double left_width = m_label_width + slider_with + item_in_gap; + + bool m_imperial_units = false; + + float value = value_in; + if (m_imperial_units) value *= float(units_mm_to_in); + float old_val = value; + + constexpr float UndefMinVal = -0.1f; + + const BoundingBoxf3 bbox = bounding_box(); + float mean_size = float((bbox.size().x() + bbox.size().y() + bbox.size().z()) / 9.0); + float min_size = value_in < 0.f ? UndefMinVal : 2.f; + if (m_imperial_units) { + mean_size *= float(units_mm_to_in); + min_size *= float(units_mm_to_in); + } + std::string format = value_in < 0.f ? " " : m_imperial_units ? "%.4f " + _u8L("in") : "%.2f " + _u8L("mm"); + + m_imgui->bbl_slider_float_style(("##" + label).c_str(), &value, min_size, mean_size, format.c_str()); + + ImGui::SameLine(left_width); + ImGui::PushItemWidth(first_input_width); + ImGui::BBLDragFloat(("##input_" + label).c_str(), &value, 0.05f, min_size, mean_size, format.c_str()); + + value_in = value * float(m_imperial_units ? units_in_to_mm : 1.0); + + left_width += (first_input_width + item_out_gap); + ImGui::SameLine(left_width); + ImGui::PushItemWidth(slider_with); + + float old_tolerance, tolerance = old_tolerance = tolerance_in * 100.f; + std::string format_t = tolerance_in < 0.f ? " " : "%.f %%"; + float min_tolerance = tolerance_in < 0.f ? UndefMinVal : 0.f; + + m_imgui->bbl_slider_float_style(("##tolerance_" + label).c_str(), &tolerance, min_tolerance, 20.f, format_t.c_str(), 1.f, true, _L("Tolerance")); + + left_width += (slider_with + item_in_gap); + ImGui::SameLine(left_width); + ImGui::PushItemWidth(second_input_width); + ImGui::BBLDragFloat(("##tolerance_input_" + label).c_str(), &tolerance, 0.05f, min_tolerance, 20.f, format_t.c_str()); + + tolerance_in = tolerance * 0.01f; + + return !is_approx(old_val, value) || !is_approx(old_tolerance, tolerance); +} + +bool GLGizmoAdvancedCut::cut_line_processing() const +{ + return m_cut_line_begin != Vec3d::Zero(); +} + +void GLGizmoAdvancedCut::discard_cut_line_processing() +{ + m_cut_line_begin = m_cut_line_end = Vec3d::Zero(); +} + +bool GLGizmoAdvancedCut::process_cut_line(SLAGizmoEventType action, const Vec2d &mouse_position) +{ + const Camera &camera = wxGetApp().plater()->get_camera(); + + Vec3d pt; + Vec3d dir; + MeshRaycaster::line_from_mouse_pos_static(mouse_position, Transform3d::Identity(), camera, pt, dir); + dir.normalize(); + pt += dir; // Move the pt along dir so it is not clipped. + + if (action == SLAGizmoEventType::LeftDown && !cut_line_processing()) { + m_cut_line_begin = pt; + m_cut_line_end = pt; + return true; + } + + if (cut_line_processing()) { + m_cut_line_end = pt; + if (action == SLAGizmoEventType::LeftDown || action == SLAGizmoEventType::LeftUp) { + Vec3d line_dir = m_cut_line_end - m_cut_line_begin; + if (line_dir.norm() < 3.0) + return true; + Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Cut by line"); + + Vec3d cross_dir = line_dir.cross(dir).normalized(); + Eigen::Quaterniond q; + Transform3d m = Transform3d::Identity(); + m.matrix().block(0, 0, 3, 3) = q.setFromTwoVectors(m_cut_plane_normal, cross_dir).toRotationMatrix(); + + m_rotate_matrix = m; + + const ModelObject * mo = m_c->selection_info()->model_object(); + const ModelInstance *mi = mo->instances[m_c->selection_info()->get_active_instance()]; + Vec3d plane_center = get_plane_center(); + + auto update_plane_after_line_cut = [this](const Vec3d &deta_plane_center, const Transform3d& rotate_matrix) { + Vec3d plane_center = get_plane_center(); + + std::array plane_points_rot; + for (int i = 0; i < plane_points_rot.size(); i++) { + plane_points_rot[i] = m_cut_plane_points[i] - plane_center; + } + + for (uint32_t i = 0; i < plane_points_rot.size(); ++i) { + plane_points_rot[i] = rotate_matrix * plane_points_rot[i]; + } + + for (int i = 0; i < plane_points_rot.size(); i++) { + m_cut_plane_points[i] = plane_points_rot[i] + plane_center + deta_plane_center; + } + }; + + update_plane_after_line_cut(cross_dir * (cross_dir.dot(pt - m_cut_plane_center)), m_rotate_matrix); + + discard_cut_line_processing(); + } else if (action == SLAGizmoEventType::Dragging) + this->set_dirty(); + return true; + } + return false; +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.hpp index a040c8accf..5cb9e765cc 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.hpp @@ -3,9 +3,15 @@ #include "GLGizmoBase.hpp" #include "GLGizmoRotate.hpp" +#include "libslic3r/Model.hpp" namespace Slic3r { +enum class CutConnectorType : int; +class ModelVolume; +struct CutConnectorAttributes; + namespace GUI { +enum class SLAGizmoEventType : unsigned char; class GLGizmoAdvancedCut : public GLGizmoRotate3D { @@ -43,7 +49,11 @@ private: bool m_keep_upper; bool m_keep_lower; bool m_cut_to_parts; - bool m_rotate_lower; + bool m_place_on_cut_upper{true}; + bool m_place_on_cut_lower{false}; + bool m_rotate_upper{false}; + bool m_rotate_lower{false}; + bool m_do_segment; double m_segment_smoothing_alpha; int m_segment_number; @@ -54,25 +64,94 @@ private: unsigned int m_last_active_id; + bool m_connectors_editing{false}; + bool m_show_shortcuts{false}; + + std::vector> m_shortcuts; + double m_label_width{150.0}; + double m_control_width{ 200.0 }; + double m_editing_window_width; + + CutConnectorType m_connector_type; + size_t m_connector_style; + size_t m_connector_shape_id; + + float m_connector_depth_ratio{3.f}; + float m_connector_depth_ratio_tolerance{0.1f}; + + float m_connector_size{2.5f}; + float m_connector_size_tolerance{0.f}; + + TriangleMesh m_connector_mesh; + bool m_has_invalid_connector{false}; + + // remember the connectors which is selected + mutable std::vector m_selected; + int m_selected_count{0}; + + Vec3d m_cut_plane_center{Vec3d::Zero()}; + Vec3d m_cut_plane_normal{Vec3d::UnitZ()}; + + Vec3d m_cut_line_begin{Vec3d::Zero()}; + Vec3d m_cut_line_end{Vec3d::Zero()}; + + Transform3d m_rotate_matrix{Transform3d::Identity()}; + + std::map m_shapes; + + struct InvalidConnectorsStatistics + { + unsigned int outside_cut_contour; + unsigned int outside_bb; + bool is_overlap; + + void invalidate() + { + outside_cut_contour = 0; + outside_bb = 0; + is_overlap = false; + } + } m_info_stats; + + //GLSelectionRectangle m_selection_rectangle; + public: GLGizmoAdvancedCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + bool gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_position, bool shift_down, bool alt_down, bool control_down); + bool on_key(wxKeyEvent &evt); + double get_movement() const { return m_movement; } void set_movement(double movement) const; void finish_rotation(); std::string get_tooltip() const override; + BoundingBoxf3 bounding_box() const; + //BoundingBoxf3 transformed_bounding_box(const Vec3d &plane_center, bool revert_move = false) const; + + bool is_looking_forward() const; + + bool unproject_on_cut_plane(const Vec2d &mouse_pos, Vec3d &pos, Vec3d &pos_world); + + virtual bool apply_clipping_plane() { return m_connectors_editing; } + protected: virtual bool on_init(); + virtual void on_load(cereal::BinaryInputArchive &ar) override; + virtual void on_save(cereal::BinaryOutputArchive &ar) const override; virtual std::string on_get_name() const; virtual void on_set_state(); virtual bool on_is_activable() const; - virtual void on_start_dragging(); + virtual CommonGizmosDataID on_get_requirements() const override; + virtual void on_start_dragging() override; + virtual void on_stop_dragging() override; virtual void on_update(const UpdateData& data); virtual void on_render(); virtual void on_render_for_picking(); virtual void on_render_input_window(float x, float y, float bottom_limit); + void show_tooltip_information(float x, float y); + virtual void on_enable_grabber(unsigned int id) { if (id < 3) @@ -97,6 +176,12 @@ protected: private: void perform_cut(const Selection& selection); + bool can_perform_cut() const; + void apply_connectors_in_model(ModelObject *mo, bool &create_dowels_as_separate_object); + + bool is_selection_changed(bool alt_down, bool shift_down); + void select_connector(int idx, bool select); + double calc_projection(const Linef3& mouse_ray) const; Vec3d calc_plane_normal(const std::array& plane_points) const; Vec3d calc_plane_center(const std::array& plane_points) const; @@ -107,6 +192,46 @@ private: std::array get_plane_points_world_coord() const; void reset_cut_plane(); void reset_all(); + + // update the connectors position so that the connectors are on the cut plane + void put_connectors_on_cut_plane(const Vec3d &cp_normal, double cp_offset); + void update_clipper(); + // on render + void render_cut_plane_and_grabbers(); + void render_connectors(); + void render_clipper_cut(); + void render_cut_line(); + void render_connector_model(GLModel &model, const std::array& color, Transform3d view_model_matrix, bool for_picking = false); + + void clear_selection(); + void init_connector_shapes(); + void set_connectors_editing(bool connectors_editing); + void reset_connectors(); + void update_connector_shape(); + void apply_selected_connectors(std::function apply_fn); + void select_all_connectors(); + void unselect_all_connectors(); + void validate_connector_settings(); + bool add_connector(CutConnectors &connectors, const Vec2d &mouse_position); + bool delete_selected_connectors(); + bool is_outside_of_cut_contour(size_t idx, const CutConnectors &connectors, const Vec3d cur_pos); + bool is_conflict_for_connector(size_t idx, const CutConnectors &connectors, const Vec3d cur_pos); + void check_conflict_for_all_connectors(); + + // render input window + void render_cut_plane_input_window(float x, float y, float bottom_limit); + void init_connectors_input_window_data(); + void render_connectors_input_window(float x, float y, float bottom_limit); + void render_input_window_warning() const; + bool render_reset_button(const std::string &label_id, const std::string &tooltip) const; + bool render_connect_type_radio_button(CutConnectorType type); + + bool render_combo(const std::string &label, const std::vector &lines, size_t &selection_idx); + bool render_slider_double_input(const std::string &label, float &value_in, float &tolerance_in); + + bool cut_line_processing() const; + void discard_cut_line_processing(); + bool process_cut_line(SLAGizmoEventType action, const Vec2d &mouse_position); }; } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp index 6d4359d437..ba8fd5280b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp @@ -162,6 +162,8 @@ public: virtual std::string get_action_snapshot_name() { return "Gizmo action"; } void set_common_data_pool(CommonGizmosDataPool* ptr) { m_c = ptr; } + virtual bool apply_clipping_plane() { return true; } + unsigned int get_sprite_id() const { return m_sprite_id; } int get_hover_id() const { return m_hover_id; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 3bced6a352..39bc98d7fb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -336,8 +336,8 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos if (action == SLAGizmoEventType::LeftUp) { if (m_wait_for_up_event) { m_wait_for_up_event = false; - return true; } + return true; } // dragging the selection rectangle: diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index 7fcb4efb73..65479b064e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -83,7 +83,7 @@ bool GLGizmoRotate::on_init() void GLGizmoRotate::on_start_dragging() { const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box(); - m_center = box.center(); + m_center = m_custom_center == Vec3d::Zero() ? box.center() : m_custom_center; m_radius = Offset + box.radius(); m_snap_coarse_in_radius = m_radius / 3.0f; m_snap_coarse_out_radius = 2.0f * m_snap_coarse_in_radius; @@ -135,7 +135,7 @@ void GLGizmoRotate::on_render() const BoundingBoxf3& box = selection.get_bounding_box(); if (m_hover_id != 0 && !m_grabbers[0].dragging) { - m_center = box.center(); + m_center = m_custom_center == Vec3d::Zero() ? box.center() : m_custom_center; m_radius = Offset + box.radius(); m_snap_coarse_in_radius = m_radius / 3.0f; m_snap_coarse_out_radius = 2.0f * m_snap_coarse_in_radius; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp index 25b8eaf533..9862ec2901 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp @@ -34,6 +34,7 @@ private: Axis m_axis; double m_angle; + mutable Vec3d m_custom_center{Vec3d::Zero()}; mutable Vec3d m_center; mutable float m_radius; @@ -52,6 +53,8 @@ public: std::string get_tooltip() const override; + void set_center(const Vec3d &point) { m_custom_center = point; } + protected: bool on_init() override; std::string on_get_name() const override { return ""; } @@ -101,6 +104,13 @@ public: return tooltip; } + void set_center(const Vec3d &point) + { + m_gizmos[X].set_center(point); + m_gizmos[Y].set_center(point); + m_gizmos[Z].set_center(point); + } + protected: bool on_init() override; std::string on_get_name() const override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index 6f61f7ebac..0f98c5dd35 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -63,6 +63,12 @@ std::string GLGizmoScale3D::get_tooltip() const return ""; } +void GLGizmoScale3D::enable_ununiversal_scale(bool enable) +{ + for (unsigned int i = 0; i < 6; ++i) + m_grabbers[i].enabled = enable; +} + bool GLGizmoScale3D::on_init() { for (int i = 0; i < 10; ++i) @@ -228,9 +234,11 @@ void GLGizmoScale3D::on_render() // BBS: when select multiple objects, uniform scale can be deselected, display the connection(4,5) //if (single_instance || single_volume) { + + if (m_grabbers[4].enabled && m_grabbers[5].enabled) { glsafe(::glColor4fv(m_grabbers[4].color.data())); render_grabbers_connection(4, 5); - //} + } glsafe(::glColor4fv(m_grabbers[2].color.data())); render_grabbers_connection(6, 7); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp index e1c72370ff..839c7f6823 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp @@ -55,6 +55,7 @@ public: std::string get_tooltip() const override; + void enable_ununiversal_scale(bool enable); protected: virtual bool on_init() override; virtual std::string on_get_name() const override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 7c95d087f7..73ed826fc6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -427,8 +427,8 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous if (action == SLAGizmoEventType::LeftUp) { if (m_wait_for_up_event) { m_wait_for_up_event = false; - return true; } + return true; } // dragging the selection rectangle: diff --git a/src/slic3r/GUI/Gizmos/GLGizmoText.cpp b/src/slic3r/GUI/Gizmos/GLGizmoText.cpp index 59ce16362d..19902d0fdb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoText.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoText.cpp @@ -21,6 +21,7 @@ #endif #include #include "libslic3r/SVG.hpp" +#include namespace Slic3r { namespace GUI { @@ -228,13 +229,6 @@ bool GLGizmoText::gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_posit const ModelInstance *mi = mo->instances[selection.get_instance_idx()]; const Camera & camera = wxGetApp().plater()->get_camera(); - // Precalculate transformations of individual meshes. - std::vector trafo_matrices; - for (const ModelVolume *mv : mo->volumes) { - if (mv->is_model_part()) { - trafo_matrices.emplace_back(mi->get_transformation().get_matrix() * mv->get_matrix()); - } - } if (action == SLAGizmoEventType::Moving) { if (shift_down && !alt_down && !control_down) { float angle = m_rotate_angle + 0.5 * (m_mouse_position - mouse_position).y(); @@ -265,6 +259,23 @@ bool GLGizmoText::gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_posit if (m_is_modify) return true; + Plater *plater = wxGetApp().plater(); + if (!plater) + return true; + + ModelObject *model_object = selection.get_model()->objects[m_object_idx]; + if (m_preview_text_volume_id > 0) { + model_object->delete_volume(m_preview_text_volume_id); + plater->update(); + m_preview_text_volume_id = -1; + } + + // Precalculate transformations of individual meshes. + std::vector trafo_matrices; + for (const ModelVolume *mv : mo->volumes) { + if (mv->is_model_part()) { trafo_matrices.emplace_back(mi->get_transformation().get_matrix() * mv->get_matrix()); } + } + Vec3f normal = Vec3f::Zero(); Vec3f hit = Vec3f::Zero(); size_t facet = 0; @@ -275,9 +286,6 @@ bool GLGizmoText::gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_posit // Cast a ray on all meshes, pick the closest hit and save it for the respective mesh for (int mesh_id = 0; mesh_id < int(trafo_matrices.size()); ++mesh_id) { - if (mesh_id == m_preview_text_volume_id) - continue; - MeshRaycaster mesh_raycaster = MeshRaycaster(mo->volumes[mesh_id]->mesh()); if (mesh_raycaster.unproject_on_mesh(mouse_position, trafo_matrices[mesh_id], camera, hit, normal, @@ -302,17 +310,6 @@ bool GLGizmoText::gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_posit m_rr = {mouse_position, closest_hit_mesh_id, closest_hit, closest_normal}; - Plater *plater = wxGetApp().plater(); - if (!plater) - return true; - - ModelObject *model_object = selection.get_model()->objects[m_object_idx]; - if (m_preview_text_volume_id > 0) { - model_object->delete_volume(m_preview_text_volume_id); - plater->update(); - m_preview_text_volume_id = -1; - } - m_is_modify = true; generate_text_volume(false); plater->update(); @@ -937,10 +934,9 @@ bool GLGizmoText::update_text_positions(const std::vector& texts) } else { alpha = texts[i]; } - TriangleMesh mesh; - load_text_shape(alpha.c_str(), m_font_name.c_str(), m_font_size, m_thickness + m_embeded_depth, m_bold, m_italic, mesh); - auto center = mesh.bounding_box().center(); - double half_x_length = center.x(); + TextResult text_result; + load_text_shape(alpha.c_str(), m_font_name.c_str(), m_font_size, m_thickness + m_embeded_depth, m_bold, m_italic, text_result); + double half_x_length = text_result.text_width / 2; text_lengths.emplace_back(half_x_length); } @@ -978,20 +974,25 @@ bool GLGizmoText::update_text_positions(const std::vector& texts) TriangleMesh slice_meshs; int mesh_index = 0; + int volume_index = 0; for (int i = 0; i < mo->volumes.size(); ++i) { + // skip the editing text volume + if (m_is_modify && m_volume_idx == i) + continue; + ModelVolume *mv = mo->volumes[i]; if (mv->is_model_part()) { if (mesh_index == m_rr.mesh_id) { - TriangleMesh vol_mesh(mv->mesh()); - vol_mesh.transform(mv->get_matrix()); - slice_meshs = vol_mesh; - break; + volume_index = i; } + TriangleMesh vol_mesh(mv->mesh()); + vol_mesh.transform(mv->get_matrix()); + slice_meshs.merge(vol_mesh); mesh_index++; } } - ModelVolume* volume = mo->volumes[mesh_index]; + ModelVolume* volume = mo->volumes[volume_index]; Vec3d temp_position = m_mouse_position_world; Vec3d temp_normal = m_mouse_normal_world; @@ -1093,7 +1094,7 @@ bool GLGizmoText::update_text_positions(const std::vector& texts) // for debug //export_regions_to_svg(Point(m_mouse_position_world.x(), m_mouse_position_world.y()), temp_polys); - Polygons polys = temp_polys; + Polygons polys = union_(temp_polys); auto point_in_line_rectange = [](const Line &line, const Point &point, double& distance) { distance = abs((point.x() - line.a.x()) * (line.b.y() - line.a.y()) - (line.b.x() - line.a.x()) * (point.y() - line.a.y())); @@ -1354,13 +1355,13 @@ bool GLGizmoText::update_text_positions(const std::vector& texts) TriangleMesh GLGizmoText::get_text_mesh(const char* text_str, const Vec3d &position, const Vec3d &normal, const Vec3d& text_up_dir) { - TriangleMesh mesh; - load_text_shape(text_str, m_font_name.c_str(), m_font_size, m_thickness + m_embeded_depth, m_bold, m_italic, mesh); + TextResult text_result; + load_text_shape(text_str, m_font_name.c_str(), m_font_size, m_thickness + m_embeded_depth, m_bold, m_italic, text_result); + TriangleMesh mesh = text_result.text_mesh; auto center = mesh.bounding_box().center(); double mesh_offset = center.z(); - - mesh.translate(-center.x(), -m_font_size / 4, -center.z()); + mesh.translate(-text_result.text_width / 2, -m_font_size / 4, -center.z()); double phi; Vec3d rotation_axis; @@ -1377,6 +1378,11 @@ TriangleMesh GLGizmoText::get_text_mesh(const char* text_str, const Vec3d &posit Vec3d new_text_dir = project_on_plane(text_up_dir, normal); new_text_dir.normalize(); Geometry::rotation_from_two_vectors(old_text_dir, new_text_dir, rotation_axis, phi, &rotation_matrix); + + static double const PI = 3.141592653589793238; + if (abs(phi - PI) < EPSILON) + rotation_axis = normal; + mesh.rotate(phi, rotation_axis); const Selection & selection = m_parent.get_selection(); @@ -1438,10 +1444,11 @@ void GLGizmoText::generate_text_volume(bool is_temp) if (text.empty()) return; + std::wstring_convert> str_cnv; + std::wstring ws = boost::nowide::widen(m_text); std::vector alphas; - if (!get_utf8_sub_strings(m_text, strlen(m_text), alphas)) { - BOOST_LOG_TRIVIAL(info) << boost::format("Text: input text is not utf8"); - return; + for (auto w : ws) { + alphas.push_back(str_cnv.to_bytes(w)); } update_text_positions(alphas); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index d52969ba5b..524264cd3d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -449,7 +449,26 @@ void ObjectClipper::set_position(double pos, bool keep_normal) get_pool()->get_canvas()->set_as_dirty(); } +void ObjectClipper::set_range_and_pos(const Vec3d &cpl_normal, double cpl_offset, double pos) +{ + m_clp.reset(new ClippingPlane(cpl_normal, cpl_offset)); + m_clp_ratio = pos; + get_pool()->get_canvas()->set_as_dirty(); +} +bool ObjectClipper::is_projection_inside_cut(const Vec3d &point) const +{ + return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [point](const auto &cl) { + return cl->is_projection_inside_cut(point); + }); +} + +bool ObjectClipper::has_valid_contour() const +{ + return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [](const auto &cl) { + return cl->has_valid_contour(); + }); +} void SupportsClipper::on_update() { diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index 91c1bde44c..f4211dfb59 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -266,6 +266,10 @@ public: ClippingPlane* get_clipping_plane() const { return m_clp.get(); } void render_cut() const; + void set_range_and_pos(const Vec3d &cpl_normal, double cpl_offset, double pos); + + bool is_projection_inside_cut(const Vec3d &point_in) const; + bool has_valid_contour() const; protected: void on_update() override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index f1ee1753ed..7575d1dd7d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -627,6 +627,8 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p return dynamic_cast(m_gizmos[MmuSegmentation].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else if (m_current == Text) return dynamic_cast(m_gizmos[Text].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); + else if (m_current == Cut) + return dynamic_cast(m_gizmos[Cut].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else return false; } @@ -872,7 +874,8 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) m_tooltip.clear(); if (evt.LeftDown() && (!control_down || grabber_contains_mouse())) { - if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation || m_current == Text) + if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || + m_current == Seam || m_current == MmuSegmentation || m_current == Text || m_current == Cut) && gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown(), evt.AltDown())) // the gizmo got the event and took some action, there is no need to do anything more processed = true; @@ -902,7 +905,8 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) // event was taken care of by the SlaSupports gizmo processed = true; } - else if (evt.RightDown() && !control_down && selected_object_idx != -1 && (m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) + else if (evt.RightDown() && !control_down && selected_object_idx != -1 + && (m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation || m_current == Cut) && gizmo_event(SLAGizmoEventType::RightDown, mouse_pos)) { // event was taken care of by the FdmSupports / Seam / MMUPainting gizmo processed = true; @@ -911,7 +915,8 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation)) // don't allow dragging objects with the Sla gizmo on processed = true; - else if (evt.Dragging() && !control_down && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) + else if (evt.Dragging() && !control_down + && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation || m_current == Cut) && gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, evt.ShiftDown(), evt.AltDown())) { // the gizmo got the event and took some action, no need to do anything more here m_parent.set_as_dirty(); @@ -924,10 +929,12 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) else if (evt.RightIsDown()) gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), true); } - else if (evt.LeftUp() && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) && !m_parent.is_mouse_dragging()) { + else if (evt.LeftUp() + && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation || m_current == Cut) + && !m_parent.is_mouse_dragging() + && gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), control_down)) { // in case SLA/FDM gizmo is selected, we just pass the LeftUp event and stop processing - neither // object moving or selecting is suppressed in that case - gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), control_down); processed = true; } else if (evt.LeftUp() && m_current == Flatten && m_gizmos[m_current]->get_hover_id() != -1) { @@ -937,8 +944,9 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) //wxGetApp().obj_manipul()->set_dirty(); processed = true; } - else if (evt.RightUp() && (m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) && !m_parent.is_mouse_dragging()) { - gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), control_down); + else if (evt.RightUp() && (m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation || m_current == Cut) + && !m_parent.is_mouse_dragging() + && gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), control_down)) { processed = true; } else if (evt.LeftUp()) { @@ -1017,8 +1025,6 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) break; } //skip some keys when gizmo - case 'r': - case 'R': case 'A': case 'a': { @@ -1088,9 +1094,9 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) // BBS: Skip all keys when in gizmo. This is necessary for 3D text tool. default: { - if (is_running() && m_current == EType::Text) { - processed = true; - } + //if (is_running() && m_current == EType::Text) { + // processed = true; + //} break; } } @@ -1113,6 +1119,13 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) const int keyCode = evt.GetKeyCode(); bool processed = false; + // todo: zhimin Each gizmo should handle key event in it own on_key() function + if (m_current == Cut) { + if (GLGizmoAdvancedCut *gizmo_cut = dynamic_cast(get_current())) { + return gizmo_cut->on_key(evt); + } + } + if (evt.GetEventType() == wxEVT_KEY_UP) { if (m_current == SlaSupports || m_current == Hollow) @@ -1481,6 +1494,11 @@ GLGizmoBase* GLGizmosManager::get_current() const return ((m_current == Undefined) || m_gizmos.empty()) ? nullptr : m_gizmos[m_current].get(); } +GLGizmoBase* GLGizmosManager::get_gizmo(GLGizmosManager::EType type) const +{ + return ((type == Undefined) || m_gizmos.empty()) ? nullptr : m_gizmos[type].get(); +} + GLGizmosManager::EType GLGizmosManager::get_gizmo_from_name(const std::string& gizmo_name) const { std::vector selectable_idxs = get_selectable_idxs(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index c943bc3888..4d13c3d07f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -230,6 +230,7 @@ public: EType get_current_type() const { return m_current; } GLGizmoBase* get_current() const; + GLGizmoBase *get_gizmo(GLGizmosManager::EType type) const; EType get_gizmo_from_name(const std::string& gizmo_name) const; bool is_running() const; @@ -311,7 +312,7 @@ public: //BBS: GUI refactor: GLToolbar adjust float get_scaled_total_height() const; float get_scaled_total_width() const; - //GizmoObjectManipulation& get_object_manipulation() { return m_object_manipulation; } + GizmoObjectManipulation& get_object_manipulation() { return m_object_manipulation; } bool get_uniform_scaling() const { return m_object_manipulation.get_uniform_scaling();} private: diff --git a/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp b/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp index b6968abe9f..7c7b158d08 100644 --- a/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp +++ b/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp @@ -330,8 +330,11 @@ void GizmoObjectManipulation::change_size_value(int axis, double value) const Selection& selection = m_glcanvas.get_selection(); Vec3d ref_size = m_cache.size; - if (selection.is_single_volume() || selection.is_single_modifier()) + if (selection.is_single_volume() || selection.is_single_modifier()) { + Vec3d instance_scale = wxGetApp().model().objects[selection.get_volume(*selection.get_volume_idxs().begin())->object_idx()]->instances[0]->get_transformation().get_scaling_factor(); ref_size = selection.get_volume(*selection.get_volume_idxs().begin())->bounding_box().size(); + ref_size = Vec3d(instance_scale[0] * ref_size[0], instance_scale[1] * ref_size[1], instance_scale[2] * ref_size[2]); + } else if (selection.is_single_full_instance()) ref_size = m_world_coordinates ? selection.get_unscaled_instance_bounding_box().size() : diff --git a/src/slic3r/GUI/IMSlider.cpp b/src/slic3r/GUI/IMSlider.cpp index 4272a72d4e..7cdfa29ce5 100644 --- a/src/slic3r/GUI/IMSlider.cpp +++ b/src/slic3r/GUI/IMSlider.cpp @@ -67,6 +67,8 @@ static std::string gcode(Type type) switch (type) { //BBS case Template: return config.opt_string("template_custom_gcode"); + case PausePrint: return config.opt_string("machine_pause_gcode"); + default: return ""; } @@ -150,6 +152,7 @@ bool IMSlider::init_texture() result &= IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/one_layer_off_dark.svg", 28, 28, m_one_layer_off_dark_id); result &= IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/one_layer_off_hover_dark.svg", 28, 28, m_one_layer_off_hover_dark_id); result &= IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/im_gcode_pause.svg", 14, 14, m_pause_icon_id); + result &= IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/im_gcode_custom.svg", 14, 14, m_custom_icon_id); result &= IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/im_slider_delete.svg", 14, 14, m_delete_icon_id); } @@ -224,6 +227,8 @@ void IMSlider::SetTicksValues(const Info &custom_gcode_per_print_z) return; } + static bool last_spiral_vase_status = false; + const bool was_empty = m_ticks.empty(); m_ticks.ticks.clear(); @@ -242,6 +247,30 @@ void IMSlider::SetTicksValues(const Info &custom_gcode_per_print_z) post_ticks_changed_event(); } + if (last_spiral_vase_status != m_is_spiral_vase) { + last_spiral_vase_status = m_is_spiral_vase; + if (!m_ticks.empty()) { + m_ticks.ticks.clear(); + post_ticks_changed_event(); + } + } + + //auto has_tick_execpt = [this](CustomGCode::Type type) { + // for (const TickCode& tick : m_ticks.ticks) + // if (tick.type != type) return true; + + // return false; + //}; + if ((!m_ticks.empty() /*&& has_tick_execpt(PausePrint)*/) && m_draw_mode == dmSequentialFffPrint) { + for (auto it{ m_ticks.ticks.begin() }, end{ m_ticks.ticks.end() }; it != end;) { + if (true/*it->type != PausePrint*/) + it = m_ticks.ticks.erase(it); + else + ++it; + } + post_ticks_changed_event(); + } + if (custom_gcode_per_print_z.mode && !custom_gcode_per_print_z.gcodes.empty()) m_ticks.mode = custom_gcode_per_print_z.mode; set_as_dirty(); @@ -282,6 +311,7 @@ void IMSlider::SetDrawMode(bool is_sequential_print) { m_draw_mode = is_sequential_print ? dmSequentialFffPrint : dmRegular; + m_can_change_color = m_can_change_color && !(m_draw_mode == dmSequentialFffPrint); } void IMSlider::SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, const int only_extruder, bool can_change_color) @@ -292,18 +322,12 @@ void IMSlider::SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, UseDefaultColors(m_mode == SingleExtruder); - auto curr_plate = wxGetApp().plater()->get_partplate_list().get_curr_plate(); - auto curr_print_seq = curr_plate->get_real_print_seq(); - - if (curr_print_seq == PrintSequence::ByObject) - m_is_wipe_tower = false; - else - m_is_wipe_tower = m_mode != SingleExtruder; + m_is_wipe_tower = m_mode != SingleExtruder; auto config = wxGetApp().preset_bundle->full_config(); m_is_spiral_vase = config.option("spiral_mode")->value; - m_can_change_color = can_change_color; + m_can_change_color = can_change_color && !m_is_spiral_vase; // close opened menu window after reslice m_show_menu = false; @@ -376,6 +400,11 @@ void IMSlider::add_code_as_tick(Type type, int selected_extruder) post_ticks_changed_event(type); } +void IMSlider::delete_tick(const TickCode& tick) { + m_ticks.ticks.erase(tick); + post_ticks_changed_event(tick.type); +} + bool IMSlider::check_ticks_changed_event(Type type) { //BBL only support MultiExtruder @@ -623,12 +652,16 @@ void IMSlider::draw_ticks(const ImRect& slideable_region) { if (ImGui::IsMouseHoveringRect(tick_hover_box.Min, tick_hover_box.Max)) { // render left tick box - ImGui::RenderFrame(tick_hover_box.Min, { slideable_region.Min.x, tick_hover_box.Max.y }, tick_hover_box_clr, false); + ImRect left_hover_box = ImRect(tick_hover_box.Min, { slideable_region.Min.x, tick_hover_box.Max.y }); + ImGui::RenderFrame(left_hover_box.Min, left_hover_box.Max, tick_hover_box_clr, false); // render right tick box - ImGui::RenderFrame({ slideable_region.Max.x, tick_hover_box.Min.y }, tick_hover_box.Max, tick_hover_box_clr, false); + ImRect right_hover_box = ImRect({ slideable_region.Max.x, tick_hover_box.Min.y }, tick_hover_box.Max); + ImGui::RenderFrame(right_hover_box.Min, right_hover_box.Max, tick_hover_box_clr, false); + + show_tooltip(*tick_it); if (context.IO.MouseClicked[0]) { - m_tick_value = tick_it->tick; - m_tick_rect = ImVec4(tick_hover_box.Min.x, tick_hover_box.Min.y, tick_hover_box.Max.x, tick_hover_box.Max.y); + m_tick_value = tick_it->tick; + m_tick_rect = ImVec4(tick_hover_box.Min.x, tick_hover_box.Min.y, tick_hover_box.Max.x, tick_hover_box.Max.y); } } ++tick_it; @@ -651,6 +684,11 @@ void IMSlider::draw_ticks(const ImRect& slideable_region) { ImVec2 icon_pos = ImVec2(slideable_region.GetCenter().x + icon_offset.x, tick_pos - icon_offset.y); button_with_pos(pause_icon_id, icon_size, icon_pos); } + if (tick_it->type == Custom || tick_it->type == Template) { + ImTextureID custom_icon_id = m_custom_icon_id; + ImVec2 icon_pos = ImVec2(slideable_region.GetCenter().x + icon_offset.x, tick_pos - icon_offset.y); + button_with_pos(custom_icon_id, icon_size, icon_pos); + } ++tick_it; } @@ -664,13 +702,45 @@ void IMSlider::draw_ticks(const ImRect& slideable_region) { if (ImGui::IsMouseHoveringRect(icon_pos, icon_pos + icon_size)) { if (context.IO.MouseClicked[0]) { // delete tick - Type type = tick_it->type; - m_ticks.ticks.erase(tick_it); - post_ticks_changed_event(type); + delete_tick(*tick_it); } } } +} +void IMSlider::show_tooltip(const std::string tooltip) { + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 6 * m_scale, 3 * m_scale }); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, { 3 * m_scale }); + ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND); + ImGui::PushStyleColor(ImGuiCol_Border, { 0,0,0,0 }); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.00f, 1.00f, 1.00f, 1.00f)); + ImGui::BeginTooltip(); + ImGui::TextUnformatted(tooltip.c_str()); + ImGui::EndTooltip(); + ImGui::PopStyleColor(3); + ImGui::PopStyleVar(2); +} + +void IMSlider::show_tooltip(const TickCode& tick){ + switch (tick.type) + { + case CustomGCode::ColorChange: + break; + case CustomGCode::PausePrint: + show_tooltip(_u8L("Pause:") + " \"" + gcode(PausePrint) + "\""); + break; + case CustomGCode::ToolChange: + show_tooltip(_u8L("Change Filament")); + break; + case CustomGCode::Template: + show_tooltip(_u8L("Custom Template:") + " \"" + gcode(Template) + "\""); + break; + case CustomGCode::Custom: + show_tooltip(_u8L("Custom G-code:") + " \"" + tick.extra + "\""); + break; + default: + break; + } } bool IMSlider::vertical_slider(const char* str_id, int* higher_value, int* lower_value, std::string& higher_label, std::string& lower_label,int v_min, int v_max, const ImVec2& size, SelectedSlider& selection, bool one_layer_flag, float scale) @@ -790,12 +860,10 @@ bool IMSlider::vertical_slider(const char* str_id, int* higher_value, int* lower context.IO.MouseClicked[0]) m_show_menu = false; - if (!m_ticks.empty()) { - // draw ticks - draw_ticks(h_selected ? higher_slideable_region : lower_slideable_region); - // draw colored band - draw_colored_band(groove, h_selected ? higher_slideable_region : lower_slideable_region); - } + // draw ticks + draw_ticks(h_selected ? higher_slideable_region : lower_slideable_region); + // draw colored band + draw_colored_band(groove, h_selected ? higher_slideable_region : lower_slideable_region); if (!m_ticks.has_tick_with_code(ToolChange)) { // draw scroll line @@ -862,12 +930,10 @@ bool IMSlider::vertical_slider(const char* str_id, int* higher_value, int* lower ImVec2 bar_center = higher_handle.GetCenter(); - if (!m_ticks.empty()) { - // draw ticks - draw_ticks(one_slideable_region); - // draw colored band - draw_colored_band(groove, one_slideable_region); - } + // draw ticks + draw_ticks(one_slideable_region); + // draw colored band + draw_colored_band(groove, one_slideable_region); // draw handle window->DrawList->AddLine(ImVec2(mid_x - 0.5 * bar_width, handle_center.y), ImVec2(mid_x + 0.5 * bar_width, handle_center.y), handle_clr, 2 * line_width); @@ -963,7 +1029,7 @@ bool IMSlider::render(int canvas_width, int canvas_height) return result; } -void IMSlider::render_input_custom_gcode() +void IMSlider::render_input_custom_gcode(std::string custom_gcode) { if (m_show_custom_gcode_window) ImGui::OpenPopup((_u8L("Custom G-code")).c_str()); @@ -995,9 +1061,13 @@ void IMSlider::render_input_custom_gcode() if (set_focus && !ImGui::IsAnyItemActive() && !ImGui::IsMouseClicked(0)) { wxGetApp().plater()->get_current_canvas3D()->force_set_focus(); ImGui::SetKeyboardFocusHere(0); + strcpy(m_custom_gcode, custom_gcode.c_str()); } const int text_height = 6; - ImGui::InputTextMultiline("##text", m_custom_gcode, sizeof(m_custom_gcode), ImVec2(-1, ImGui::GetTextLineHeight() * text_height)); + + ImGui::InputTextMultiline("##text", m_custom_gcode, sizeof(m_custom_gcode), ImVec2(-1, ImGui::GetTextLineHeight() * text_height), ImGuiInputTextFlags_CallbackAlways, [](ImGuiInputTextCallbackData* data) { + return data->CursorPos = data->BufTextLen; + }); ImGui::NewLine(); ImGui::SameLine(ImGui::GetStyle().WindowPadding.x * 14); @@ -1046,7 +1116,7 @@ void IMSlider::do_go_to_layer(size_t layer_number) { void IMSlider::render_go_to_layer_dialog() { if (m_show_go_to_layer_dialog) - ImGui::OpenPopup((_u8L("Jump to layer")).c_str()); + ImGui::OpenPopup((_u8L("Jump to Layer")).c_str()); ImGuiWrapper& imgui = *wxGetApp().imgui(); ImVec2 center = ImGui::GetMainViewport()->GetCenter(); @@ -1123,37 +1193,61 @@ void IMSlider::render_go_to_layer_dialog() imgui.pop_menu_style(); } -void IMSlider::render_menu() -{ - ImGuiWrapper::push_menu_style(m_scale); - int extruder_num = m_extruder_colors.size(); +void IMSlider::render_menu() { + if (!m_menu_enable) + return; - if (m_show_menu) { - ImGui::OpenPopup("slider_menu_popup"); + ImGuiWrapper::push_menu_style(m_scale); + ImGui::PushStyleVar(ImGuiStyleVar_::ImGuiStyleVar_ChildRounding, 4.0f * m_scale); + + auto tick_it = GetSelection() == ssHigher ? m_ticks.ticks.find(TickCode{ GetHigherValue() }) : + GetSelection() == ssLower ? m_ticks.ticks.find(TickCode{ GetLowerValue() }) : + m_ticks.ticks.end(); + std::string custom_code; + if (tick_it != m_ticks.ticks.end()) { + render_edit_menu(*tick_it); + if (tick_it->type == CustomGCode::Custom) + custom_code = tick_it->extra; + } + else { + render_add_menu(); } - ImGui::PushStyleVar(ImGuiStyleVar_::ImGuiStyleVar_ChildRounding, 4.0f * m_scale); - if (ImGui::BeginPopup("slider_menu_popup")) { + ImGui::PopStyleVar(1); + ImGuiWrapper::pop_menu_style(); + + render_input_custom_gcode(custom_code); + render_go_to_layer_dialog(); +} + +void IMSlider::render_add_menu() +{ + int extruder_num = m_extruder_colors.size(); + + if (m_show_menu) + ImGui::OpenPopup("slider_add_menu_popup"); + if (ImGui::BeginPopup("slider_add_menu_popup")) { bool menu_item_enable = m_draw_mode != dmSequentialFffPrint; - //if ((m_selection == ssLower && GetLowerValueD() == m_zero_layer_height) || (m_selection == ssHigher && GetHigherValueD() == m_zero_layer_height)) - //{ - // if (menu_item_with_icon(_u8L("Jump to Layer").c_str(), "")) { - // m_show_go_to_layer_dialog = true; - // } - //} - //else + bool hovered = false; { - if (menu_item_with_icon(_u8L("Add Pause").c_str(), "", ImVec2(0, 0), 0, false, menu_item_enable)) { + if (menu_item_with_icon(_u8L("Add Pause").c_str(), "", ImVec2(0, 0), 0, false, menu_item_enable, &hovered)) { add_code_as_tick(PausePrint); } - if (menu_item_with_icon(_u8L("Add Custom G-code").c_str(), "", ImVec2(0, 0), 0, false, menu_item_enable)) { + if (hovered) { show_tooltip(_u8L("Insert a pause command at the beginning of this layer.")); } + + + if (menu_item_with_icon(_u8L("Add Custom G-code").c_str(), "", ImVec2(0, 0), 0, false, menu_item_enable, &hovered)) { m_show_custom_gcode_window = true; } + if (hovered) { show_tooltip(_u8L("Insert custom G-code at the beginning of this layer.")); } + if (!gcode(Template).empty()) { - if (menu_item_with_icon(_u8L("Add Custom Template").c_str(), "", ImVec2(0, 0), 0, false, menu_item_enable)) { + if (menu_item_with_icon(_u8L("Add Custom Template").c_str(), "", ImVec2(0, 0), 0, false, menu_item_enable, &hovered)) { add_code_as_tick(Template); } + if (hovered) { show_tooltip(_u8L("Insert template custom G-code at the beginning of this layer.")); } } + if (menu_item_with_icon(_u8L("Jump to Layer").c_str(), "")) { m_show_go_to_layer_dialog = true; } @@ -1161,26 +1255,78 @@ void IMSlider::render_menu() //BBS render this menu item only when extruder_num > 1 if (extruder_num > 1) { - if (!m_can_change_color || m_draw_mode == dmSequentialFffPrint || m_is_spiral_vase) { + if (!m_can_change_color) { begin_menu(_u8L("Change Filament").c_str(), false); } else if (begin_menu(_u8L("Change Filament").c_str())) { for (int i = 0; i < extruder_num; i++) { std::array rgba = decode_color_to_float_array(m_extruder_colors[i]); ImU32 icon_clr = IM_COL32(rgba[0] * 255.0f, rgba[1] * 255.0f, rgba[2] * 255.0f, rgba[3] * 255.0f); - if (menu_item_with_icon((_u8L("Filament ") + std::to_string(i + 1)).c_str(), "", ImVec2(14, 14) * m_scale, icon_clr)) add_code_as_tick(ToolChange, i + 1); + if (menu_item_with_icon((_u8L("Filament ") + std::to_string(i + 1)).c_str(), "", ImVec2(14, 14) * m_scale, icon_clr, false, true, &hovered)) add_code_as_tick(ToolChange, i + 1); + if (hovered) { show_tooltip(_u8L("Change filament at the beginning of this layer.")); } } end_menu(); } } + ImGui::EndPopup(); } - ImGui::PopStyleVar(1); +} - ImGuiWrapper::pop_menu_style(); - - render_input_custom_gcode(); - render_go_to_layer_dialog(); +void IMSlider::render_edit_menu(const TickCode& tick) +{ + if (m_show_menu) + ImGui::OpenPopup("slider_edit_menu_popup"); + if (ImGui::BeginPopup("slider_edit_menu_popup")) { + switch (tick.type) + { + case CustomGCode::PausePrint: + if (menu_item_with_icon(_u8L("Delete Pause").c_str(), "")) { + delete_tick(tick); + } + break; + case CustomGCode::Template: + if (!gcode(Template).empty()) { + if (menu_item_with_icon(_u8L("Delete Custom Template").c_str(), "")) { + delete_tick(tick); + } + } + break; + case CustomGCode::Custom: + if (menu_item_with_icon(_u8L("Edit Custom G-code").c_str(), "")) { + m_show_custom_gcode_window = true; + } + if (menu_item_with_icon(_u8L("Delete Custom G-code").c_str(), "")) { + delete_tick(tick); + } + break; + case CustomGCode::ToolChange: { + int extruder_num = m_extruder_colors.size(); + if (extruder_num > 1) { + if (!m_can_change_color) { + begin_menu(_u8L("Change Filament").c_str(), false); + } + else if (begin_menu(_u8L("Change Filament").c_str())) { + for (int i = 0; i < extruder_num; i++) { + std::array rgba = decode_color_to_float_array(m_extruder_colors[i]); + ImU32 icon_clr = IM_COL32(rgba[0] * 255.0f, rgba[1] * 255.0f, rgba[2] * 255.0f, rgba[3] * 255.0f); + if (menu_item_with_icon((_u8L("Filament ") + std::to_string(i + 1)).c_str(), "", ImVec2(14, 14) * m_scale, icon_clr)) add_code_as_tick(ToolChange, i + 1); + } + end_menu(); + } + if (menu_item_with_icon(_u8L("Delete Filament Change").c_str(), "")) { + delete_tick(tick); + } + } + break; + } + case CustomGCode::ColorChange: + case CustomGCode::Unknown: + default: + break; + } + ImGui::EndPopup(); + } } void IMSlider::on_change_color_mode(bool is_dark) { @@ -1314,16 +1460,8 @@ std::string IMSlider::get_label(int tick, LabelType label_type) if (label_type == ltHeightWithLayer) { char buffer[64]; size_t layer_number; - if (m_values[GetMinValueD()] == m_zero_layer_height) { - layer_number = m_is_wipe_tower ? get_layer_number(value, label_type): (m_values.empty() ? value : value); - m_values[value] == m_zero_layer_height ? - ::sprintf(buffer, "%5s\n%5s", _u8L("Start").c_str(), _u8L("G-code").c_str()) : - ::sprintf(buffer, "%5s\n%5s", std::to_string(layer_number).c_str(), layer_height); - } - else { - layer_number = m_is_wipe_tower ? get_layer_number(value, label_type) + 1 : (m_values.empty() ? value : value + 1); - ::sprintf(buffer, "%5s\n%5s", std::to_string(layer_number).c_str(), layer_height); - } + layer_number = m_draw_mode == dmSequentialFffPrint ? (m_values.empty() ? value : value + 1) : m_is_wipe_tower ? get_layer_number(value, label_type) + 1 : (m_values.empty() ? value : value + 1); + ::sprintf(buffer, "%5s\n%5s", std::to_string(layer_number).c_str(), layer_height); return std::string(buffer); } } diff --git a/src/slic3r/GUI/IMSlider.hpp b/src/slic3r/GUI/IMSlider.hpp index 7d1e10c6c1..a2fe6c0798 100644 --- a/src/slic3r/GUI/IMSlider.hpp +++ b/src/slic3r/GUI/IMSlider.hpp @@ -51,7 +51,6 @@ enum LabelType ltEstimatedTime, }; - class IMSlider { public: @@ -129,20 +128,26 @@ public: float m_scale = 1.0; void set_scale(float scale = 1.0); void on_change_color_mode(bool is_dark); + void set_menu_enable(bool enable = true) { m_menu_enable = enable; } protected: void add_custom_gcode(std::string custom_gcode); void add_code_as_tick(Type type, int selected_extruder = -1); - void do_go_to_layer(size_t layer_number); + void delete_tick(const TickCode& tick); + void do_go_to_layer(size_t layer_number); //menu void correct_lower_value(); void correct_higher_value(); bool horizontal_slider(const char* str_id, int* v, int v_min, int v_max, const ImVec2& size, float scale = 1.0); - void render_go_to_layer_dialog(); - void render_input_custom_gcode(); + void render_go_to_layer_dialog(); //menu + void render_input_custom_gcode(std::string custom_gcode = ""); //menu void render_menu(); + void render_add_menu(); //menu + void render_edit_menu(const TickCode& tick); //menu void draw_background_and_groove(const ImRect& bg_rect, const ImRect& groove); void draw_colored_band(const ImRect& groove, const ImRect& slideable_region); void draw_ticks(const ImRect& slideable_region); + void show_tooltip(const TickCode& tick); //menu + void show_tooltip(const std::string tooltip); //menu bool vertical_slider(const char* str_id, int* higher_value, int* lower_value, std::string& higher_label, std::string& lower_label, int v_min, int v_max, const ImVec2& size, @@ -175,21 +180,14 @@ private: bool m_render_as_disabled{ false }; SelectedSlider m_selection; - bool m_is_left_down = false; - bool m_is_right_down = false; bool m_is_one_layer = false; - bool m_is_focused = false; - bool m_show_menu = false; - bool m_show_custom_gcode_window = false; - bool m_show_go_to_layer_dialog = false; + bool m_menu_enable = true; //menu + bool m_show_menu = false; //menu + bool m_show_custom_gcode_window = false; //menu + bool m_show_go_to_layer_dialog = false; //menu bool m_force_mode_apply = true; - bool m_enable_action_icon = true; - bool m_enable_cog_icon = false; bool m_is_wipe_tower = false; // This flag indicates that there is multiple extruder print with wipe tower bool m_is_spiral_vase = false; - bool m_display_lower = true; - bool m_display_higher = true; - int m_selected_tick_value = -1; /* BBS slider images */ void *m_one_layer_on_id; @@ -205,6 +203,7 @@ private: void* m_one_layer_off_dark_id; void* m_one_layer_off_hover_dark_id; void *m_pause_icon_id; + void *m_custom_icon_id; void *m_delete_icon_id; DrawMode m_draw_mode = dmRegular; @@ -228,8 +227,8 @@ private: std::vector m_alternate_values; - char m_custom_gcode[1024] = { 0 }; - char m_layer_number[64] = { 0 }; + char m_custom_gcode[1024] = { 0 }; //menu + char m_layer_number[64] = { 0 }; //menu }; } diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 547f718c5e..75450237f3 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -84,6 +84,10 @@ static const std::map font_icons = { {ImGui::TextSearchIcon , "im_text_search" }, {ImGui::TextSearchCloseIcon , "im_text_search_close" }, + + {ImGui::ExpandBtn , "expand_btn" }, + {ImGui::CollapseBtn , "collapse_btn" }, + {ImGui::RevertBtn , "revert_btn" }, }; static const std::map font_icons_large = { {ImGui::CloseNotifButton , "notification_close" }, @@ -120,7 +124,7 @@ const ImVec4 ImGuiWrapper::COL_GREY_DARK = { 0.333f, 0.333f, 0.333f, 1.0 const ImVec4 ImGuiWrapper::COL_GREY_LIGHT = { 0.4f, 0.4f, 0.4f, 1.0f }; const ImVec4 ImGuiWrapper::COL_ORANGE_DARK = { 0.757f, 0.404f, 0.216f, 1.0f }; const ImVec4 ImGuiWrapper::COL_ORANGE_LIGHT = { 1.0f, 0.49f, 0.216f, 1.0f }; -const ImVec4 ImGuiWrapper::COL_WINDOW_BACKGROUND = { 0.133f, 0.133f, 0.133f, 0.8f }; +const ImVec4 ImGuiWrapper::COL_WINDOW_BACKGROUND = { 0.1f, 0.1f, 0.1f, 0.8f }; const ImVec4 ImGuiWrapper::COL_BUTTON_BACKGROUND = COL_ORANGE_DARK; const ImVec4 ImGuiWrapper::COL_BUTTON_HOVERED = COL_ORANGE_LIGHT; const ImVec4 ImGuiWrapper::COL_BUTTON_ACTIVE = ImGuiWrapper::COL_BUTTON_HOVERED; @@ -1115,7 +1119,7 @@ bool ImGuiWrapper::undo_redo_list(const ImVec2& size, const bool is_undo, bool ( // To do that we push a ColorMarkerHovered symbol at the very beginning of the label // This symbol will be used to a color selection for the highlighted letters. // see imgui_draw.cpp, void ImFont::RenderText() -static bool selectable(const char* label, bool selected, ImGuiSelectableFlags flags = 0, const ImVec2& size_arg = ImVec2(0, 0)) +static bool selectable(const char* label, bool selected, ImGuiSelectableFlags flags = 0, const ImVec2& size_arg = ImVec2(0, 0), bool* out_hovered = nullptr) { ImGuiWindow* window = ImGui::GetCurrentWindow(); if (window->SkipItems) @@ -1261,10 +1265,11 @@ static bool selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (flags & ImGuiSelectableFlags_Disabled) ImGui::PopStyleColor(); if (hovered || selected) ImGui::PopStyleColor(); + if (out_hovered) *out_hovered = hovered; + // Automatically close popups if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(g.CurrentItemFlags & ImGuiItemFlags_SelectableDontClosePopup)) ImGui::CloseCurrentPopup(); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags); return pressed; } @@ -1433,7 +1438,7 @@ void end_menu() ImGui::EndMenu(); } -bool menu_item_with_icon(const char *label, const char *shortcut, ImVec2 icon_size /* = ImVec2(0, 0)*/, ImU32 icon_color /* = 0*/, bool selected /* = false*/, bool enabled /* = true*/) +bool menu_item_with_icon(const char *label, const char *shortcut, ImVec2 icon_size /* = ImVec2(0, 0)*/, ImU32 icon_color /* = 0*/, bool selected /* = false*/, bool enabled /* = true*/, bool* hovered/* = nullptr*/) { ImGuiWindow *window = ImGui::GetCurrentWindow(); if (window->SkipItems) return false; @@ -1466,7 +1471,7 @@ bool menu_item_with_icon(const char *label, const char *shortcut, ImVec2 icon_si float shortcut_w = shortcut ? ImGui::CalcTextSize(shortcut, NULL).x : 0.0f; float min_w = window->DC.MenuColumns.DeclColumns(label_size.x, shortcut_w, IM_FLOOR(g.FontSize * 1.20f)); // Feedback for next frame float extra_w = std::max(0.0f, ImGui::GetContentRegionAvail().x - min_w); - pressed = selectable(label, false, flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f)); + pressed = selectable(label, false, flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f), hovered); if (icon_size.x != 0 && icon_size.y != 0) { float selectable_pos_y = pos.y + -0.5f * style.ItemSpacing.y; @@ -1665,7 +1670,24 @@ void ImGuiWrapper::bold_text(const std::string& str) text(str); } } - +bool ImGuiWrapper::push_bold_font() { + if (bold_font) { + ImGui::PushFont(bold_font); + return true; + } + else { + return false; + } +} +bool ImGuiWrapper::pop_bold_font() { + if (bold_font) { + ImGui::PopFont(); + return true; + } + else { + return false; + } +} bool ImGuiWrapper::push_font_by_name(std::string font_name) { auto sys_font = im_fonts_map.find(font_name); @@ -2002,6 +2024,37 @@ void ImGuiWrapper::pop_button_disable_style() { ImGui::PopStyleColor(3); } +void ImGuiWrapper::push_combo_style(const float scale) +{ + if (m_is_dark_mode) { + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 1.0f * scale); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f * scale); + ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BG_DARK); + ImGui::PushStyleColor(ImGuiCol_BorderActive, ImVec4(0.00f, 0.68f, 0.26f, 1.00f)); + ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.00f, 0.68f, 0.26f, 0.0f)); + ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.00f, 0.68f, 0.26f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.00f, 0.68f, 0.26f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ScrollbarBg, ImGuiWrapper::COL_WINDOW_BG_DARK); + ImGui::PushStyleColor(ImGuiCol_Button, {1.00f, 1.00f, 1.00f, 0.0f}); + } else { + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 1.0f * scale); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f * scale); + ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BG); + ImGui::PushStyleColor(ImGuiCol_BorderActive, ImVec4(0.00f, 0.68f, 0.26f, 1.00f)); + ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.00f, 0.68f, 0.26f, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.00f, 0.68f, 0.26f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.00f, 0.68f, 0.26f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ScrollbarBg, ImGuiWrapper::COL_WINDOW_BG); + ImGui::PushStyleColor(ImGuiCol_Button, {1.00f, 1.00f, 1.00f, 0.0f}); + } +} + +void ImGuiWrapper::pop_combo_style() +{ + ImGui::PopStyleVar(2); + ImGui::PopStyleColor(7); +} + void ImGuiWrapper::init_font(bool compress) { destroy_font(); @@ -2026,7 +2079,7 @@ void ImGuiWrapper::init_font(bool compress) cfg.OversampleH = cfg.OversampleV = 1; //FIXME replace with io.Fonts->AddFontFromMemoryTTF(buf_decompressed_data, (int)buf_decompressed_size, m_font_size, nullptr, ranges.Data); //https://github.com/ocornut/imgui/issues/220 - default_font = io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/" + "HarmonyOS_Sans_SC_Regular.ttf").c_str(), m_font_size, &cfg, ImGui::GetIO().Fonts->GetGlyphRangesChineseFull()); + default_font = io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/" + "HarmonyOS_Sans_SC_Regular.ttf").c_str(), m_font_size, &cfg, m_font_cjk ? ImGui::GetIO().Fonts->GetGlyphRangesChineseFull() : ranges.Data); if (default_font == nullptr) { default_font = io.Fonts->AddFontDefault(); if (default_font == nullptr) { diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 001f9bea49..052ea00e5e 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -39,7 +39,7 @@ bool button_with_pos(ImTextureID user_texture_id, const ImVec2 &margin = ImVec2(0, 0)); bool begin_menu(const char *label, bool enabled = true); void end_menu(); -bool menu_item_with_icon(const char *label, const char *shortcut, ImVec2 icon_size = ImVec2(0, 0), ImU32 icon_color = 0, bool selected = false, bool enabled = true); +bool menu_item_with_icon(const char *label, const char *shortcut, ImVec2 icon_size = ImVec2(0, 0), ImU32 icon_color = 0, bool selected = false, bool enabled = true, bool* hovered = nullptr); class ImGuiWrapper @@ -160,6 +160,8 @@ public: // set font const std::vector get_fonts_names() const { return m_fonts_names; } + bool push_bold_font(); + bool pop_bold_font(); bool push_font_by_name(std::string font_name); bool pop_font_by_name(std::string font_name); void load_fonts_texture(); @@ -213,6 +215,8 @@ public: static void pop_cancel_button_style(); static void push_button_disable_style(); static void pop_button_disable_style(); + static void push_combo_style(const float scale); + static void pop_combo_style(); //BBS static int TOOLBAR_WINDOW_FLAGS; diff --git a/src/slic3r/GUI/ImageGrid.cpp b/src/slic3r/GUI/ImageGrid.cpp index eff08335d7..49e02c85f4 100644 --- a/src/slic3r/GUI/ImageGrid.cpp +++ b/src/slic3r/GUI/ImageGrid.cpp @@ -5,6 +5,7 @@ #include "I18N.hpp" #include "GUI_App.hpp" #include "GUI.hpp" +#include "MsgDialog.hpp" #include @@ -68,6 +69,7 @@ void ImageGrid::SetFileSystem(boost::shared_ptr file_sys) m_row_count = 0; m_col_count = 1; m_row_offset = 0; + m_scroll_offset = 0; UpdateFileSystem(); } @@ -102,6 +104,7 @@ void Slic3r::GUI::ImageGrid::SetGroupMode(int mode) m_row_offset = index / m_col_count * 4; if (m_row_offset >= m_row_count) m_row_offset = m_row_count == 0 ? 0 : m_row_count - 1; + m_scroll_offset = 0; } void Slic3r::GUI::ImageGrid::SetSelecting(bool selecting) @@ -127,11 +130,12 @@ void Slic3r::GUI::ImageGrid::Rescale() void Slic3r::GUI::ImageGrid::Select(size_t index) { + if (m_selecting) { + m_file_sys->ToggleSelect(index); + Refresh(); + return; + } if (m_file_sys->GetGroupMode() == PrinterFileSystem::G_NONE) { - if (m_selecting) { - m_file_sys->ToggleSelect(index); - Refresh(); - } return; } index = m_file_sys->EnterSubGroup(index); @@ -139,12 +143,20 @@ void Slic3r::GUI::ImageGrid::Select(size_t index) m_row_offset = index / m_col_count * 4; if (m_row_offset >= m_row_count) m_row_offset = m_row_count == 0 ? 0 : m_row_count - 1; + m_scroll_offset = 0; Refresh(); } void Slic3r::GUI::ImageGrid::DoAction(size_t index, int action) { if (action == 0) { + if (m_file_sys->GetSelectCount() > 1) { + MessageDialog dlg(this, + wxString::Format(_L("You are going to delete %u files. Are you sure to continue?"), m_file_sys->GetSelectCount()), + _L("Delete files"), wxYES_NO | wxICON_WARNING); + if (dlg.ShowModal() != wxID_YES) + return; + } m_file_sys->DeleteFiles(index); } else if (action == 1) { if (index != -1) { @@ -152,7 +164,9 @@ void Slic3r::GUI::ImageGrid::DoAction(size_t index, int action) if (file.IsDownload() && file.progress >= -1) { if (file.progress >= 100) { if (!m_file_sys->DownloadCheckFile(index)) { - wxMessageBox(wxString::Format(_L("File '%s' was lost! Please download it again."), from_u8(file.name)), _L("Error"), wxOK); + MessageDialog(this, + wxString::Format(_L("File '%s' was lost! Please download it again."), from_u8(file.name)), + _L("Error"), wxOK).ShowModal(); Refresh(); return; } @@ -176,7 +190,9 @@ void Slic3r::GUI::ImageGrid::DoAction(size_t index, int action) if (file.IsDownload() && file.progress >= -1) { if (file.progress >= 100) { if (!m_file_sys->DownloadCheckFile(index)) { - wxMessageBox(wxString::Format(_L("File '%s' was lost! Please download it again."), from_u8(file.name)), _L("Error"), wxOK); + MessageDialog(this, + wxString::Format(_L("File '%s' was lost! Please download it again."), from_u8(file.name)), + _L("Error"), wxOK).ShowModal(); Refresh(); return; } @@ -231,6 +247,7 @@ void ImageGrid::UpdateLayout() m_row_count = nrow > 0 ? nrow + 1 : 0; if (m_row_offset >= m_row_count) m_row_offset = m_row_count == 0 ? 0 : m_row_count - 1; + m_scroll_offset = 0; // create mask if (m_file_sys->GetGroupMode() == PrinterFileSystem::G_NONE) { mask_size.x = (m_col_count - 1) * m_cell_size.GetWidth() + m_image_size.GetWidth(); @@ -369,13 +386,14 @@ void ImageGrid::resize(wxSizeEvent& event) void ImageGrid::mouseWheelMoved(wxMouseEvent &event) { auto delta = -event.GetWheelRotation(); - m_scroll_offset += delta; - int max = m_row_count * m_cell_size.GetHeight() / 4; - if (m_scroll_offset < 0) - m_scroll_offset = 0; - else if (m_scroll_offset >= max) - m_scroll_offset = max - 1; - m_row_offset = m_scroll_offset * 4 / m_cell_size.GetHeight(); + m_scroll_offset += delta * 4; + delta = m_scroll_offset / m_cell_size.GetHeight(); + m_row_offset += delta; + if (m_row_offset < 0) + m_row_offset = 0; + else if (m_row_offset >= m_row_count) + m_row_offset = m_row_count == 0 ? 0 : m_row_count - 1; + m_scroll_offset -= delta * m_cell_size.GetHeight(); m_timer.StartOnce(4000); // Show position bar UpdateFocusRange(); Refresh(); @@ -465,6 +483,8 @@ wxBitmap Slic3r::GUI::ImageGrid::createCircleBitmap(wxSize size, int borderWidth return bmp; } +static constexpr wchar_t const *TIME_FORMATS[] = {_T("%Y-%m-%d"), _T("%Y-%m"), _T("%Y")}; + /* * Here we do the actual rendering. I put it in a separate * method so that it can work no matter what type of DC @@ -495,7 +515,6 @@ void ImageGrid::render(wxDC& dc) // Draw line spacing at top if (off.y > 0) dc.DrawRectangle({0, 0, size.x, off.y}); - constexpr wchar_t const * formats[] = {_T("%Y-%m-%d"), _T("%Y-%m"), _T("%Y")}; size_t start = index; size_t end = index; size_t hit_image = m_selecting ? size_t(-1) : m_hit_type == HIT_ITEM ? m_hit_item : m_hit_type == HIT_ACTION ? m_hit_item / 4 :size_t(-1); @@ -506,65 +525,7 @@ void ImageGrid::render(wxDC& dc) wxPoint pt{off.x, off.y}; end = (index + m_col_count) < m_file_sys->GetCount() ? index + m_col_count : m_file_sys->GetCount(); while (index < end) { - auto & file = m_file_sys->GetFile(index); - // Draw thumbnail - if (file.thumbnail.IsOk()) { - float hs = (float) m_image_size.GetWidth() / file.thumbnail.GetWidth(); - float vs = (float) m_image_size.GetHeight() / file.thumbnail.GetHeight(); - dc.SetUserScale(hs, vs); - dc.DrawBitmap(file.thumbnail, {(int) (pt.x / hs), (int) (pt.y / vs)}); - dc.SetUserScale(1, 1); - if (m_file_sys->GetGroupMode() != PrinterFileSystem::G_NONE) { - dc.DrawBitmap(m_mask, pt); - } - } - bool show_download_state_always = true; - // Draw checked icon - if (m_selecting && !show_download_state_always) - dc.DrawBitmap(file.IsSelect() ? m_checked_icon.bmp() : m_unchecked_icon.bmp(), - pt + wxPoint{10, m_image_size.GetHeight() - m_checked_icon.GetBmpHeight() - 10}); - // can't handle alpha - // dc.GradientFillLinear({pt.x, pt.y, m_image_size.GetWidth(), 60}, wxColour(0x6F, 0x6F, 0x6F, 0x99), wxColour(0x6F, 0x6F, 0x6F, 0), wxBOTTOM); - else if (m_file_sys->GetGroupMode() == PrinterFileSystem::G_NONE) { - wxString nonHoverText; - wxString secondAction = _L("Download"); - wxString thirdAction; - int states = 0; - // Draw download progress - if (file.IsDownload()) { - if (file.progress == -1) { - secondAction = _L("Cancel"); - nonHoverText = _L("Download waiting..."); - } else if (file.progress < 0) { - secondAction = _L("Retry"); - nonHoverText = _L("Download failed"); - states = StateColor::Checked; - } else if (file.progress >= 100) { - secondAction = _L("Play"); - thirdAction = _L("Open Folder"); - nonHoverText = _L("Download finished"); - } else { - secondAction = _L("Cancel"); - nonHoverText = wxString::Format(_L("Downloading %d%%..."), file.progress); - thirdAction = wxString::Format(L"%d%%...", file.progress); - } - } - // Draw buttons on hovered item - wxRect rect{pt.x, pt.y + m_image_size.y - m_buttons_background.GetHeight(), m_image_size.GetWidth(), m_buttons_background.GetHeight()}; - if (hit_image == index) { - renderButtons(dc, {_L("Delete"), (wxChar const *) secondAction, thirdAction.IsEmpty() ? nullptr : (wxChar const *) thirdAction, nullptr}, rect, - m_hit_type == HIT_ACTION ? m_hit_item & 3 : -1, states); - } else if (!nonHoverText.IsEmpty()) { - renderButtons(dc, {(wxChar const *) nonHoverText, nullptr}, rect, -1, states); - } - if (m_selecting && show_download_state_always) - dc.DrawBitmap(file.IsSelect() ? m_checked_icon.bmp() : m_unchecked_icon.bmp(), - pt + wxPoint{10, m_image_size.GetHeight() - m_checked_icon.GetBmpHeight() - 10}); - } else { - dc.SetTextForeground(*wxWHITE); // time text color - auto date = wxDateTime((time_t) file.time).Format(_L(formats[m_file_sys->GetGroupMode()])); - dc.DrawText(date, pt + wxPoint{24, 16}); - } + renderContent(dc, pt, index, hit_image == index); // Draw colume spacing at right dc.DrawRectangle({pt.x + m_image_size.GetWidth(), pt.y, m_cell_size.GetWidth() - m_image_size.GetWidth(), m_image_size.GetHeight()}); ++index; @@ -583,8 +544,8 @@ void ImageGrid::render(wxDC& dc) dc.DrawRectangle({off.x, 0}, m_mask.GetSize()); auto & file1 = m_file_sys->GetFile(start); auto & file2 = m_file_sys->GetFile(end - 1); - auto date1 = wxDateTime((time_t) file1.time).Format(_L(formats[m_file_sys->GetGroupMode()])); - auto date2 = wxDateTime((time_t) file2.time).Format(_L(formats[m_file_sys->GetGroupMode()])); + auto date1 = wxDateTime((time_t) file1.time).Format(_L(TIME_FORMATS[m_file_sys->GetGroupMode()])); + auto date2 = wxDateTime((time_t) file2.time).Format(_L(TIME_FORMATS[m_file_sys->GetGroupMode()])); dc.SetFont(Label::Head_16); dc.SetTextForeground(StateColor::darkModeColorFor("#262E30")); dc.DrawText(date1 + " - " + date2, wxPoint{off.x, 2}); @@ -605,6 +566,68 @@ void ImageGrid::render(wxDC& dc) } } +void Slic3r::GUI::ImageGrid::renderContent(wxDC &dc, wxPoint const &pt, int index, bool hit) +{ + bool selected = false; + auto &file = m_file_sys->GetFile(index, selected); + // Draw thumbnail + if (file.thumbnail.IsOk()) { + float hs = (float) m_image_size.GetWidth() / file.thumbnail.GetWidth(); + float vs = (float) m_image_size.GetHeight() / file.thumbnail.GetHeight(); + dc.SetUserScale(hs, vs); + dc.DrawBitmap(file.thumbnail, {(int) (pt.x / hs), (int) (pt.y / vs)}); + dc.SetUserScale(1, 1); + if (m_file_sys->GetGroupMode() != PrinterFileSystem::G_NONE) { + dc.DrawBitmap(m_mask, pt); + } + } + bool show_download_state_always = true; + // Draw checked icon + if (m_selecting && !show_download_state_always) + dc.DrawBitmap(selected ? m_checked_icon.bmp() : m_unchecked_icon.bmp(), pt + wxPoint{10, m_image_size.GetHeight() - m_checked_icon.GetBmpHeight() - 10}); + // can't handle alpha + // dc.GradientFillLinear({pt.x, pt.y, m_border_size.GetWidth(), 60}, wxColour(0x6F, 0x6F, 0x6F, 0x99), wxColour(0x6F, 0x6F, 0x6F, 0), wxBOTTOM); + else if (m_file_sys->GetGroupMode() == PrinterFileSystem::G_NONE) { + wxString nonHoverText; + wxString secondAction = _L("Download"); + wxString thirdAction; + int states = 0; + // Draw download progress + if (file.IsDownload()) { + if (file.progress == -1) { + secondAction = _L("Cancel"); + nonHoverText = _L("Download waiting..."); + } else if (file.progress < 0) { + secondAction = _L("Retry"); + nonHoverText = _L("Download failed"); + states = StateColor::Checked; + } else if (file.progress >= 100) { + secondAction = _L("Play"); + thirdAction = _L("Open Folder"); + nonHoverText = _L("Download finished"); + } else { + secondAction = _L("Cancel"); + nonHoverText = wxString::Format(_L("Downloading %d%%..."), file.progress); + thirdAction = wxString::Format(L"%d%%...", file.progress); + } + } + // Draw buttons on hovered item + wxRect rect{pt.x, pt.y + m_image_size.GetHeight() - m_buttons_background.GetHeight(), m_image_size.GetWidth(), m_buttons_background.GetHeight()}; + if (hit) { + renderButtons(dc, {_L("Delete"), (wxChar const *) secondAction, thirdAction.IsEmpty() ? nullptr : (wxChar const *) thirdAction, nullptr}, rect, + m_hit_type == HIT_ACTION ? m_hit_item & 3 : -1, states); + } else if (!nonHoverText.IsEmpty()) { + renderButtons(dc, {(wxChar const *) nonHoverText, nullptr}, rect, -1, states); + } + } else { + dc.SetTextForeground(*wxWHITE); // time text color + auto date = wxDateTime((time_t) file.time).Format(_L(TIME_FORMATS[m_file_sys->GetGroupMode()])); + dc.DrawText(date, pt + wxPoint{24, 16}); + } + if (m_selecting && show_download_state_always) + dc.DrawBitmap(selected ? m_checked_icon.bmp() : m_unchecked_icon.bmp(), pt + wxPoint{10, m_image_size.GetHeight() - m_checked_icon.GetBmpHeight() - 10}); +} + void Slic3r::GUI::ImageGrid::renderButtons(wxDC &dc, wxStringList const &texts, wxRect const &rect2, size_t hit, int states) { // Draw background diff --git a/src/slic3r/GUI/ImageGrid.h b/src/slic3r/GUI/ImageGrid.h index 8aef8a5e9d..0aa97b0080 100644 --- a/src/slic3r/GUI/ImageGrid.h +++ b/src/slic3r/GUI/ImageGrid.h @@ -76,6 +76,8 @@ protected: void render(wxDC &dc); + void renderContent(wxDC &dc, wxPoint const &pt, int index, bool hit); + void renderButtons(wxDC &dc, wxStringList const &texts, wxRect const &rect, size_t hit, int states); void renderText(wxDC &dc, wxString const & text, wxRect const & rect, int states); diff --git a/src/slic3r/GUI/Jobs/ArrangeJob.cpp b/src/slic3r/GUI/Jobs/ArrangeJob.cpp index 81537f43e3..75bf5875f1 100644 --- a/src/slic3r/GUI/Jobs/ArrangeJob.cpp +++ b/src/slic3r/GUI/Jobs/ArrangeJob.cpp @@ -241,9 +241,15 @@ void ArrangeJob::prepare_all() { } // 准备料塔。逻辑如下: -// 1. 如果料塔被禁用,或是逐件打印,则不需要料塔 -// 2. 以下两种情况需要料塔:1)某对象是多色对象;2)打开了支撑,且支撑体与接触面使用的是不同材料 -// 3. 如果允许不同材料落在相同盘,则以下情况也需要料塔:1)所有选定对象中使用了多种热床温度相同的材料(比如颜色不同的PLA) +// 1. 以下几种情况不需要料塔: +// 1)料塔被禁用, +// 2)逐件打印, +// 3)不允许不同材料落在相同盘,且没有多色对象 +// 2. 以下情况需要料塔: +// 1)某对象是多色对象; +// 2)打开了支撑,且支撑体与接触面使用的是不同材料 +// 3)允许不同材料落在相同盘,且所有选定对象中使用了多种热床温度相同的材料 +// (所有对象都是单色的,但不同对象的材料不同,例如:对象A使用红色PLA,对象B使用白色PLA) void ArrangeJob::prepare_wipe_tower() { bool need_wipe_tower = false; @@ -257,7 +263,7 @@ void ArrangeJob::prepare_wipe_tower() // need wipe tower if some object has multiple extruders (has paint-on colors or support material) for (const auto &item : m_selected) { std::set obj_extruders; - for (int id : item.extrude_ids) obj_extruders.insert(id); + obj_extruders.insert(item.extrude_ids.begin(), item.extrude_ids.end()); if (obj_extruders.size() > 1) { need_wipe_tower = true; BOOST_LOG_TRIVIAL(info) << "arrange: need wipe tower because object " << item.name << " has multiple extruders (has paint-on colors)"; @@ -266,6 +272,7 @@ void ArrangeJob::prepare_wipe_tower() } // if multile extruders have same bed temp, we need wipe tower + // 允许不同材料落在相同盘,且所有选定对象中使用了多种热床温度相同的材料 if (params.allow_multi_materials_on_same_plate) { std::map> bedTemp2extruderIds; for (const auto &item : m_selected) @@ -294,17 +301,19 @@ void ArrangeJob::prepare_wipe_tower() } // if wipe tower is not init yet (no wipe tower in any plate before arrangement) - if (wipe_tower_ap.poly.empty()) { - auto &print = wxGetApp().plater()->get_partplate_list().get_current_fff_print(); - wipe_tower_ap.poly.contour.points = print.first_layer_wipe_tower_corners(false); + //if (wipe_tower_ap.poly.empty()) { + // auto &print = wxGetApp().plater()->get_partplate_list().get_current_fff_print(); + // wipe_tower_ap.poly.contour.points = print.first_layer_wipe_tower_corners(false); wipe_tower_ap.name = "WipeTower"; wipe_tower_ap.is_virt_object = true; wipe_tower_ap.is_wipe_tower = true; - } - + //} + const GLCanvas3D* canvas3D=static_cast(m_plater->canvas3D()); for (int bedid = 0; bedid < MAX_NUM_PLATES; bedid++) { if (!plates_have_wipe_tower[bedid]) { - wipe_tower_ap.bed_idx = bedid; + wipe_tower_ap.translation = {0, 0}; + wipe_tower_ap.poly.contour.points = canvas3D->estimate_wipe_tower_points(bedid, !only_on_partplate); + wipe_tower_ap.bed_idx = bedid; m_unselected.emplace_back(wipe_tower_ap); } } @@ -502,7 +511,7 @@ void ArrangeJob::process() auto& print = wxGetApp().plater()->get_partplate_list().get_current_fff_print(); if (params.is_seq_print) - params.min_obj_distance = std::max(params.min_obj_distance, scaled(params.cleareance_radius)); + params.min_obj_distance = std::max(params.min_obj_distance, scaled(params.cleareance_radius + 0.001)); // +0.001mm to avoid clearance check fail due to rounding error if (params.avoid_extrusion_cali_region && print.full_print_config().opt_bool("scan_first_layer")) partplate_list.preprocess_nonprefered_areas(m_unselected, MAX_NUM_PLATES); @@ -750,6 +759,7 @@ void ArrangeJob::finalize() { m_plater->update(); Job::finalize(); + m_plater->m_arrange_running.store(false); } std::optional diff --git a/src/slic3r/GUI/Jobs/BindJob.cpp b/src/slic3r/GUI/Jobs/BindJob.cpp index 12bdb1b6f9..e285062685 100644 --- a/src/slic3r/GUI/Jobs/BindJob.cpp +++ b/src/slic3r/GUI/Jobs/BindJob.cpp @@ -32,10 +32,11 @@ wxString get_login_fail_reason(std::string fail_reason) return _L("Unknown Failure"); } -BindJob::BindJob(std::shared_ptr pri, Plater *plater, std::string dev_id, std::string dev_ip) +BindJob::BindJob(std::shared_ptr pri, Plater *plater, std::string dev_id, std::string dev_ip, std::string sec_link) : PlaterJob{std::move(pri), plater}, m_dev_id(dev_id), - m_dev_ip(dev_ip) + m_dev_ip(dev_ip), + m_sec_link(sec_link) { ; } @@ -79,7 +80,7 @@ void BindJob::process() long offset = tz.GetOffset(); std::string timezone = get_timezone_utc_hm(offset); - int result = m_agent->bind(m_dev_ip, timezone, + int result = m_agent->bind(m_dev_ip, m_dev_id, m_sec_link, timezone, [this, &curr_percent, &msg](int stage, int code, std::string info) { if (stage == BBL::BindJobStage::LoginStageConnect) { curr_percent = 15; diff --git a/src/slic3r/GUI/Jobs/BindJob.hpp b/src/slic3r/GUI/Jobs/BindJob.hpp index 89db7def24..4fa9e1ea11 100644 --- a/src/slic3r/GUI/Jobs/BindJob.hpp +++ b/src/slic3r/GUI/Jobs/BindJob.hpp @@ -16,13 +16,14 @@ class BindJob : public PlaterJob std::function m_success_fun{nullptr}; std::string m_dev_id; std::string m_dev_ip; + std::string m_sec_link; bool m_job_finished{ false }; int m_print_job_completed_id = 0; protected: void on_exception(const std::exception_ptr &) override; public: - BindJob(std::shared_ptr pri, Plater *plater, std::string dev_id, std::string dev_ip); + BindJob(std::shared_ptr pri, Plater *plater, std::string dev_id, std::string dev_ip, std::string sec_link); int status_range() const override { diff --git a/src/slic3r/GUI/Jobs/Job.cpp b/src/slic3r/GUI/Jobs/Job.cpp index 8224285cf3..3cc0ae5aa7 100644 --- a/src/slic3r/GUI/Jobs/Job.cpp +++ b/src/slic3r/GUI/Jobs/Job.cpp @@ -35,6 +35,11 @@ void GUI::Job::update_percent_finish() m_progress->clear_percent(); } +void GUI::Job::show_networking_test(wxString msg) +{ + m_progress->show_networking_test(msg); +} + GUI::Job::Job(std::shared_ptr pri) : m_progress(std::move(pri)) { diff --git a/src/slic3r/GUI/Jobs/Job.hpp b/src/slic3r/GUI/Jobs/Job.hpp index 4041e498f2..5c41bc7178 100644 --- a/src/slic3r/GUI/Jobs/Job.hpp +++ b/src/slic3r/GUI/Jobs/Job.hpp @@ -47,6 +47,8 @@ protected: void update_percent_finish(); + void show_networking_test(wxString msg); + bool was_canceled() const { return m_canceled.load(); } // Launched just before start(), a job can use it to prepare internals diff --git a/src/slic3r/GUI/Jobs/NotificationProgressIndicator.cpp b/src/slic3r/GUI/Jobs/NotificationProgressIndicator.cpp index e9f957e4f3..d6b4c2aca0 100644 --- a/src/slic3r/GUI/Jobs/NotificationProgressIndicator.cpp +++ b/src/slic3r/GUI/Jobs/NotificationProgressIndicator.cpp @@ -10,6 +10,11 @@ void NotificationProgressIndicator::clear_percent() } +void NotificationProgressIndicator::show_networking_test(wxString msg) +{ + +} + void NotificationProgressIndicator::set_range(int range) { m_nm->progress_indicator_set_range(range); diff --git a/src/slic3r/GUI/Jobs/NotificationProgressIndicator.hpp b/src/slic3r/GUI/Jobs/NotificationProgressIndicator.hpp index 388bfea935..f41659e3da 100644 --- a/src/slic3r/GUI/Jobs/NotificationProgressIndicator.hpp +++ b/src/slic3r/GUI/Jobs/NotificationProgressIndicator.hpp @@ -15,6 +15,7 @@ public: explicit NotificationProgressIndicator(NotificationManager *nm); void clear_percent() override; + void show_networking_test(wxString msg) override; void set_range(int range) override; void set_cancel_callback(CancelFn = CancelFn()) override; void set_progress(int pr) override; diff --git a/src/slic3r/GUI/Jobs/PrintJob.cpp b/src/slic3r/GUI/Jobs/PrintJob.cpp index ea56483f34..11a0c3631d 100644 --- a/src/slic3r/GUI/Jobs/PrintJob.cpp +++ b/src/slic3r/GUI/Jobs/PrintJob.cpp @@ -110,6 +110,7 @@ void PrintJob::process() { /* display info */ wxString msg; + wxString error_str; int curr_percent = 10; NetworkAgent* m_agent = wxGetApp().getAgent(); AppConfig* config = wxGetApp().app_config; @@ -157,6 +158,10 @@ void PrintJob::process() else curr_plate_idx = m_plater->get_partplate_list().get_curr_plate_index() + 1; + PartPlate* curr_plate = m_plater->get_partplate_list().get_curr_plate(); + if (curr_plate) { + this->task_bed_type = bed_type_to_gcode_string(curr_plate->get_bed_type(true)); + } BBL::PrintParams params; @@ -200,6 +205,7 @@ void PrintJob::process() params.ams_mapping_info = this->task_ams_mapping_info; params.connection_type = this->connection_type; params.task_use_ams = this->task_use_ams; + params.task_bed_type = this->task_bed_type; if (wxGetApp().model().model_info && wxGetApp().model().model_info.get()) { ModelInfo* model_info = wxGetApp().model().model_info.get(); auto origin_profile_id = model_info->metadata_items.find(BBL_DESIGNER_PROFILE_ID_TAG); @@ -227,19 +233,22 @@ void PrintJob::process() 30, // PrintingStageUpload 70, // PrintingStageWaiting 75, // PrintingStageRecord - 99, // PrintingStageSending + 97, // PrintingStageSending 100 // PrintingStageFinished }; - auto update_fn = [this, &msg, &curr_percent, &error_text, StagePercentPoint](int stage, int code, std::string info) { - if (stage == BBL::SendingPrintJobStage::PrintingStageCreate) { + bool is_try_lan_mode = false; + bool is_try_lan_mode_failed = false; + + auto update_fn = [this, &is_try_lan_mode, &is_try_lan_mode_failed, &msg, &error_str, &curr_percent, &error_text, StagePercentPoint](int stage, int code, std::string info) { + if (stage == BBL::SendingPrintJobStage::PrintingStageCreate && !is_try_lan_mode_failed) { if (this->connection_type == "lan") { msg = _L("Sending print job over LAN"); } else { msg = _L("Sending print job through cloud service"); } } - else if (stage == BBL::SendingPrintJobStage::PrintingStageUpload) { + else if (stage == BBL::SendingPrintJobStage::PrintingStageUpload && !is_try_lan_mode_failed) { if (code >= 0 && code <= 100 && !info.empty()) { if (this->connection_type == "lan") { msg = _L("Sending print job over LAN"); @@ -256,10 +265,10 @@ void PrintJob::process() msg = _L("Sending print job through cloud service"); } } - else if (stage == BBL::SendingPrintJobStage::PrintingStageRecord) { + else if (stage == BBL::SendingPrintJobStage::PrintingStageRecord && !is_try_lan_mode) { msg = _L("Sending print configuration"); } - else if (stage == BBL::SendingPrintJobStage::PrintingStageSending) { + else if (stage == BBL::SendingPrintJobStage::PrintingStageSending && !is_try_lan_mode) { if (this->connection_type == "lan") { msg = _L("Sending print job over LAN"); } else { @@ -289,9 +298,11 @@ void PrintJob::process() if (code > 100 || code < 0) { error_text = this->get_http_error_msg(code, info); - msg += wxString::Format("[%s]", error_text); + error_str = wxString::Format("[%s]", error_text); + } else { + error_str = wxEmptyString; } - this->update_status(curr_percent, msg); + this->update_status(curr_percent, msg + error_str); }; auto cancel_fn = [this]() { @@ -319,6 +330,7 @@ void PrintJob::process() else { BOOST_LOG_TRIVIAL(info) << "print_job: use ftp send print only"; this->update_status(curr_percent, _L("Sending print job over LAN")); + is_try_lan_mode = true; result = m_agent->start_local_print_with_record(params, update_fn, cancel_fn); if (result < 0) { error_text = wxString::Format("Access code:%s Ip address:%s", params.password, params.dev_ip); @@ -336,7 +348,10 @@ void PrintJob::process() BOOST_LOG_TRIVIAL(info) << "print_job: try to start local print with record"; this->update_status(curr_percent, _L("Sending print job over LAN")); result = m_agent->start_local_print_with_record(params, update_fn, cancel_fn); - if (result == BAMBU_NETWORK_ERR_FTP_LOGIN_DENIED) { + if (result == 0) { + params.comments = ""; + } + else if (result == BAMBU_NETWORK_ERR_FTP_LOGIN_DENIED) { params.comments = "wrong_code"; } else if (result == BAMBU_NETWORK_ERR_FTP_UPLOAD_FAILED) { @@ -346,6 +361,7 @@ void PrintJob::process() params.comments = (boost::format("failed(%1%)") % result).str(); } if (result < 0) { + is_try_lan_mode_failed = true; // try to send with cloud BOOST_LOG_TRIVIAL(warning) << "print_job: try to send with cloud"; this->update_status(curr_percent, _L("Sending print job through cloud service")); @@ -384,7 +400,7 @@ void PrintJob::process() } else if (result == BAMBU_NETWORK_ERR_TIMEOUT) { msg_text = timeout_to_upload_str; } else if (result == BAMBU_NETWORK_ERR_INVALID_RESULT) { - msg_text = _L("Failed to send the print job. Please try again."); + msg_text = _L("Failed to send the print job. Please try again."); } else if (result == BAMBU_NETWORK_ERR_FTP_UPLOAD_FAILED) { msg_text = _L("Failed to send the print job. Please try again."); } else { @@ -394,7 +410,15 @@ void PrintJob::process() curr_percent = 0; msg_text += wxString::Format("[%d][%s]", result, error_text); } - update_status(curr_percent, msg_text); + + + if (result == BAMBU_NETWORK_ERR_INVALID_RESULT) { + this->show_networking_test(msg_text); + } + else { + update_status(curr_percent, msg_text); + } + BOOST_LOG_TRIVIAL(error) << "print_job: failed, result = " << result; } else { BOOST_LOG_TRIVIAL(error) << "print_job: send ok."; diff --git a/src/slic3r/GUI/Jobs/ProgressIndicator.hpp b/src/slic3r/GUI/Jobs/ProgressIndicator.hpp index b029fa762a..cee6985966 100644 --- a/src/slic3r/GUI/Jobs/ProgressIndicator.hpp +++ b/src/slic3r/GUI/Jobs/ProgressIndicator.hpp @@ -18,6 +18,7 @@ public: virtual ~ProgressIndicator() = default; virtual void clear_percent() = 0; + virtual void show_networking_test(wxString msg) = 0; virtual void set_range(int range) = 0; virtual void set_cancel_callback(CancelFn = CancelFn()) = 0; virtual void set_progress(int pr) = 0; diff --git a/src/slic3r/GUI/Jobs/SendJob.cpp b/src/slic3r/GUI/Jobs/SendJob.cpp index eb3a065dc2..4746ab8800 100644 --- a/src/slic3r/GUI/Jobs/SendJob.cpp +++ b/src/slic3r/GUI/Jobs/SendJob.cpp @@ -162,9 +162,26 @@ void SendJob::process() PartPlate* plate = m_plater->get_partplate_list().get_plate(job_data.plate_idx); if (plate == nullptr) { - plate = m_plater->get_partplate_list().get_curr_plate(); - if (plate == nullptr) - return; + if (job_data.plate_idx == PLATE_ALL_IDX) { + //all plate + for (int index = 0; index < total_plate_num; index++) + { + PartPlate* plate_n = m_plater->get_partplate_list().get_plate(index); + if (plate_n && plate_n->is_valid_gcode_file()) + { + plate = plate_n; + break; + } + } + } + else { + plate = m_plater->get_partplate_list().get_curr_plate(); + } + if (plate == nullptr) { + BOOST_LOG_TRIVIAL(error) << "can not find plate with valid gcode file when sending to print, plate_index="<< job_data.plate_idx; + update_status(curr_percent, check_gcode_failed_str); + return; + } } /* check gcode is valid */ @@ -350,7 +367,7 @@ void SendJob::process() } if (result == BAMBU_NETWORK_ERR_WRONG_IP_ADDRESS) { - msg_text = _L("Failed uploading print file. Please enter ip address again."); + msg_text = timeout_to_upload_str; } update_status(curr_percent, msg_text); diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index 7e5b9b31cd..b07f6c48ee 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -183,7 +183,12 @@ void KBShortcutsDialog::fill_shortcuts() // Slice plate { ctrl + "R", L("Slice plate")}, // Send to Print - { ctrl + "Shift" + "G", L("Print plate")}, +#ifdef __APPLE__ + { L("⌘+Shift+G"), L("Print plate")}, +#else + { L("Ctrl+Shift+G"), L("Print plate")}, +#endif // __APPLE + // Edit { ctrl + "X", L("Cut") }, { ctrl + "C", L("Copy to clipboard") }, @@ -196,7 +201,7 @@ void KBShortcutsDialog::fill_shortcuts() #ifdef __APPLE__ {"fn+⌫", L("Delete selected")}, #else - {"Del", L("Delete selected")}, + {L("Del"), L("Delete selected")}, #endif // Help { "?", L("Show keyboard shortcuts list") } @@ -208,12 +213,12 @@ void KBShortcutsDialog::fill_shortcuts() { L("Right mouse button"), L("Pan View") }, { L("Mouse wheel"), L("Zoom View") }, { "A", L("Arrange all objects") }, - { "Shift+A", L("Arrange objects on selected plates") }, + { L("Shift+A"), L("Arrange objects on selected plates") }, //{ "R", L("Auto orientates selected objects or all objects.If there are selected objects, it just orientates the selected ones.Otherwise, it will orientates all objects in the project.") }, - {"Shift+R", L("Auto orientates selected objects or all objects.If there are selected objects, it just orientates the selected ones.Otherwise, it will orientates all objects in the current disk.")}, + {L("Shift+R"), L("Auto orientates selected objects or all objects.If there are selected objects, it just orientates the selected ones.Otherwise, it will orientates all objects in the current disk.")}, - {"Shift+Tab", L("Collapse/Expand the sidebar")}, + {L("Shift+Tab"), L("Collapse/Expand the sidebar")}, #ifdef __APPLE__ {L("⌘+Any arrow"), L("Movement in camera space")}, {L("⌥+Left mouse button"), L("Select a part")}, @@ -230,7 +235,7 @@ void KBShortcutsDialog::fill_shortcuts() {L("Arrow Left"), L("Move selection 10 mm in negative X direction")}, {L("Arrow Right"), L("Move selection 10 mm in positive X direction")}, {L("Shift+Any arrow"), L("Movement step set to 1 mm")}, - {"Esc", L("Deselect all")}, + {L("Esc"), L("Deselect all")}, {"1-9", L("keyboard 1-9: set filament for object/part")}, {ctrl + "0", L("Camera view - Default")}, {ctrl + "1", L("Camera view - Top")}, @@ -257,8 +262,8 @@ void KBShortcutsDialog::fill_shortcuts() m_full_shortcuts.push_back({ { _L("Plater"), "" }, plater_shortcuts }); Shortcuts gizmos_shortcuts = { - {"Esc", L("Deselect all")}, - {"Shift+", L("Move: press to snap by 1mm") }, + {L("Esc"), L("Deselect all")}, + {L("Shift+"), L("Move: press to snap by 1mm")}, #ifdef __APPLE__ {L("⌘+Mouse wheel"), L("Support/Color Painting: adjust pen radius")}, {L("⌥+Mouse wheel"), L("Support/Color Painting: adjust section position")}, @@ -271,8 +276,8 @@ void KBShortcutsDialog::fill_shortcuts() Shortcuts object_list_shortcuts = { {"1-9", L("Set extruder number for the objects and parts") }, - {"Del", L("Delete objects, parts, modifiers ")}, - {"Esc", L("Deselect all")}, + {L("Del"), L("Delete objects, parts, modifiers ")}, + {L("Esc"), L("Deselect all")}, {ctrl + "C", L("Copy to clipboard")}, {ctrl + "V", L("Paste from clipboard")}, {ctrl + "X", L("Cut")}, diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 2f525893c5..bc58b25fe5 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -180,6 +180,10 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_ wxGetApp().app_config->set_bool("dump_video", false); + wxString max_recent_count_str = wxGetApp().app_config->get("max_recent_count"); + long max_recent_count = 18; + if (max_recent_count_str.ToLong(&max_recent_count)) + set_max_recent_count((int)max_recent_count); //reset log level auto loglevel = wxGetApp().app_config->get("severity_level"); @@ -490,10 +494,14 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_ update_slice_print_status(eEventSliceUpdate, true, true); // BBS: backup project - std::string backup_interval; - if (!wxGetApp().app_config->get("", "backup_interval", backup_interval)) - backup_interval = "10"; - Slic3r::set_backup_interval(boost::lexical_cast(backup_interval)); + if (wxGetApp().app_config->get("backup_switch") == "true") { + std::string backup_interval; + if (!wxGetApp().app_config->get("", "backup_interval", backup_interval)) + backup_interval = "10"; + Slic3r::set_backup_interval(boost::lexical_cast(backup_interval)); + } else { + Slic3r::set_backup_interval(0); + } Slic3r::set_backup_callback([this](int action) { if (action == 0) { wxPostEvent(this, wxCommandEvent(EVT_BACKUP_POST)); @@ -808,6 +816,11 @@ void MainFrame::shutdown() m_plater->get_mouse3d_controller().save_config(*wxGetApp().app_config); } + // stop agent + NetworkAgent* agent = wxGetApp().getAgent(); + if (agent) + agent->track_enable(false); + // Stop the background thread of the removable drive manager, so that no new updates will be sent to the Plater. //wxGetApp().removable_drive_manager()->shutdown(); //stop listening for messages from other instances @@ -993,10 +1006,9 @@ void MainFrame::init_tabpanel() { }); m_printer_view->Hide(); - m_auxiliary = new AuxiliaryPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize); - m_auxiliary->SetBackgroundColour(*wxWHITE); - m_tabpanel->AddPage(m_auxiliary, _L("Project"), std::string("tab_auxiliary_avtice"), std::string("tab_auxiliary_avtice")); - + m_project = new ProjectPanel(m_tabpanel, wxID_ANY, wxDefaultPosition, wxDefaultSize); + m_project->SetBackgroundColour(*wxWHITE); + m_tabpanel->AddPage(m_project, _L("Project"), std::string("tab_auxiliary_avtice"), std::string("tab_auxiliary_avtice")); if (m_plater) { // load initial config auto full_config = wxGetApp().preset_bundle->full_config(); @@ -1131,6 +1143,7 @@ void MainFrame::create_preset_tabs() add_created_tab(new TabPrint(m_param_panel), "cog"); add_created_tab(new TabPrintObject(m_param_panel), "cog"); add_created_tab(new TabPrintPart(m_param_panel), "cog"); + add_created_tab(new TabPrintLayer(m_param_panel), "cog"); add_created_tab(new TabFilament(m_param_dialog->panel()), "spool"); /* BBS work around to avoid appearance bug */ //add_created_tab(new TabSLAPrint(m_param_panel)); @@ -1839,7 +1852,7 @@ void MainFrame::on_dpi_changed(const wxRect& suggested_rect) //BBS GUI refactor: remove unused layout new/dlg //if (m_layout != ESettingsLayout::Dlg) // Do not update tabs if the Settings are in the separated dialog m_param_panel->msw_rescale(); - m_auxiliary->msw_rescale(); + m_project->msw_rescale(); m_monitor->msw_rescale(); // BBS @@ -2009,26 +2022,27 @@ static void add_common_publish_menu_items(wxMenu* publish_menu, MainFrame* mainF static void add_common_view_menu_items(wxMenu* view_menu, MainFrame* mainFrame, std::function can_change_view) { + const wxString ctrl = _L("Ctrl+"); // The camera control accelerators are captured by GLCanvas3D::on_char(). - append_menu_item(view_menu, wxID_ANY, _L("Default View") + "\tCtrl+0", _L("Default View"), [mainFrame](wxCommandEvent&) { + append_menu_item(view_menu, wxID_ANY, _L("Default View") + "\t" + ctrl + "0", _L("Default View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("plate"); mainFrame->plater()->get_current_canvas3D()->zoom_to_bed(); }, "", nullptr, [can_change_view]() { return can_change_view(); }, mainFrame); //view_menu->AppendSeparator(); //TRN To be shown in the main menu View->Top - append_menu_item(view_menu, wxID_ANY, _L("Top") + "\tCtrl+1", _L("Top View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("top"); }, + append_menu_item(view_menu, wxID_ANY, _L("Top") + "\t" + ctrl + "1", _L("Top View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("top"); }, "", nullptr, [can_change_view]() { return can_change_view(); }, mainFrame); //TRN To be shown in the main menu View->Bottom - append_menu_item(view_menu, wxID_ANY, _L("Bottom") + "\tCtrl+2", _L("Bottom View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("bottom"); }, + append_menu_item(view_menu, wxID_ANY, _L("Bottom") + "\t" + ctrl + "2", _L("Bottom View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("bottom"); }, "", nullptr, [can_change_view]() { return can_change_view(); }, mainFrame); - append_menu_item(view_menu, wxID_ANY, _L("Front") + "\tCtrl+3", _L("Front View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("front"); }, + append_menu_item(view_menu, wxID_ANY, _L("Front") + "\t" + ctrl + "3", _L("Front View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("front"); }, "", nullptr, [can_change_view]() { return can_change_view(); }, mainFrame); - append_menu_item(view_menu, wxID_ANY, _L("Rear") + "\tCtrl+4", _L("Rear View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("rear"); }, + append_menu_item(view_menu, wxID_ANY, _L("Rear") + "\t" + ctrl + "4", _L("Rear View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("rear"); }, "", nullptr, [can_change_view]() { return can_change_view(); }, mainFrame); - append_menu_item(view_menu, wxID_ANY, _L("Left") + "\tCtrl+5", _L("Left View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("left"); }, + append_menu_item(view_menu, wxID_ANY, _L("Left") + "\t" + ctrl + "5", _L("Left View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("left"); }, "", nullptr, [can_change_view]() { return can_change_view(); }, mainFrame); - append_menu_item(view_menu, wxID_ANY, _L("Right") + "\tCtrl+6", _L("Right View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("right"); }, + append_menu_item(view_menu, wxID_ANY, _L("Right") + "\t" + ctrl + "6", _L("Right View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("right"); }, "", nullptr, [can_change_view]() { return can_change_view(); }, mainFrame); } @@ -2038,6 +2052,8 @@ void MainFrame::init_menubar_as_editor() wxMenuBar::SetAutoWindowMenu(false); m_menubar = new wxMenuBar(); #endif + + const wxString ctrl = _L("Ctrl+"); // File menu wxMenu* fileMenu = new wxMenu; @@ -2049,17 +2065,17 @@ void MainFrame::init_menubar_as_editor() []{ return true; }, this); #endif // New Project - append_menu_item(fileMenu, wxID_ANY, _L("New Project") + "\tCtrl+N", _L("Start a new project"), + append_menu_item(fileMenu, wxID_ANY, _L("New Project") + "\t" + ctrl + "N", _L("Start a new project"), [this](wxCommandEvent&) { if (m_plater) m_plater->new_project(); }, "", nullptr, [this](){return can_start_new_project(); }, this); // Open Project #ifndef __APPLE__ - append_menu_item(fileMenu, wxID_ANY, _L("Open Project") + dots + "\tCtrl+O", _L("Open a project file"), + append_menu_item(fileMenu, wxID_ANY, _L("Open Project") + dots + "\t" + ctrl + "O", _L("Open a project file"), [this](wxCommandEvent&) { if (m_plater) m_plater->load_project(); }, "menu_open", nullptr, [this](){return can_open_project(); }, this); #else - append_menu_item(fileMenu, wxID_ANY, _L("Open Project") + dots + "\tCtrl+O", _L("Open a project file"), + append_menu_item(fileMenu, wxID_ANY, _L("Open Project") + dots + "\t" + ctrl + "O", _L("Open a project file"), [this](wxCommandEvent&) { if (m_plater) m_plater->load_project(); }, "", nullptr, [this](){return can_open_project(); }, this); #endif @@ -2086,22 +2102,22 @@ void MainFrame::init_menubar_as_editor() // BBS: close save project #ifndef __APPLE__ - append_menu_item(fileMenu, wxID_ANY, _L("Save Project") + "\tCtrl+S", _L("Save current project to file"), + append_menu_item(fileMenu, wxID_ANY, _L("Save Project") + "\t" + ctrl + "S", _L("Save current project to file"), [this](wxCommandEvent&) { if (m_plater) m_plater->save_project(); }, "menu_save", nullptr, [this](){return m_plater != nullptr && can_save(); }, this); #else - append_menu_item(fileMenu, wxID_ANY, _L("Save Project") + "\tCtrl+S", _L("Save current project to file"), + append_menu_item(fileMenu, wxID_ANY, _L("Save Project") + "\t" + ctrl + "S", _L("Save current project to file"), [this](wxCommandEvent&) { if (m_plater) m_plater->save_project(); }, "", nullptr, [this](){return m_plater != nullptr && can_save(); }, this); #endif #ifndef __APPLE__ - append_menu_item(fileMenu, wxID_ANY, _L("Save Project as") + dots + "\tCtrl+Shift+S", _L("Save current project as"), + append_menu_item(fileMenu, wxID_ANY, _L("Save Project as") + dots + "\t" + ctrl + _L("Shift+") + "S", _L("Save current project as"), [this](wxCommandEvent&) { if (m_plater) m_plater->save_project(true); }, "menu_save", nullptr, [this](){return m_plater != nullptr && can_save_as(); }, this); #else - append_menu_item(fileMenu, wxID_ANY, _L("Save Project as") + dots + "\tCtrl+Shift+S", _L("Save current project as"), + append_menu_item(fileMenu, wxID_ANY, _L("Save Project as") + dots + "\t" + ctrl + _L("Shift+") + "S", _L("Save current project as"), [this](wxCommandEvent&) { if (m_plater) m_plater->save_project(true); }, "", nullptr, [this](){return m_plater != nullptr && can_save_as(); }, this); #endif @@ -2112,13 +2128,13 @@ void MainFrame::init_menubar_as_editor() // BBS wxMenu *import_menu = new wxMenu(); #ifndef __APPLE__ - append_menu_item(import_menu, wxID_ANY, _L("Import 3MF/STL/STEP/SVG/OBJ/AMF") + dots + "\tCtrl+I", _L("Load a model"), + append_menu_item(import_menu, wxID_ANY, _L("Import 3MF/STL/STEP/SVG/OBJ/AMF") + dots + "\t" + ctrl + "I", _L("Load a model"), [this](wxCommandEvent&) { if (m_plater) { m_plater->add_file(); } }, "menu_import", nullptr, [this](){return can_add_models(); }, this); #else - append_menu_item(import_menu, wxID_ANY, _L("Import 3MF/STL/STEP/SVG/OBJ/AMF") + dots + "\tCtrl+I", _L("Load a model"), + append_menu_item(import_menu, wxID_ANY, _L("Import 3MF/STL/STEP/SVG/OBJ/AMF") + dots + "\t" + ctrl + "I", _L("Load a model"), [this](wxCommandEvent&) { if (m_plater) { m_plater->add_model(); } }, "", nullptr, [this](){return can_add_models(); }, this); #endif @@ -2138,7 +2154,7 @@ void MainFrame::init_menubar_as_editor() [this](wxCommandEvent&) { if (m_plater) m_plater->export_core_3mf(); }, "menu_export_sliced_file", nullptr, [this](){return can_export_model(); }, this); // BBS export .gcode.3mf - append_menu_item(export_menu, wxID_ANY, _L("Export plate sliced file") + dots + "\tCtrl+G", _L("Export current sliced file"), + append_menu_item(export_menu, wxID_ANY, _L("Export plate sliced file") + dots + "\t" + ctrl + "G", _L("Export current sliced file"), [this](wxCommandEvent&) { if (m_plater) wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_EXPORT_SLICED_FILE)); }, "menu_export_sliced_file", nullptr, [this](){return can_export_gcode(); }, this); @@ -2180,34 +2196,41 @@ void MainFrame::init_menubar_as_editor() wxString hotkey_delete = "Del"; #endif + auto handle_key_event = [](wxKeyEvent& evt) { + if (wxGetApp().imgui()->update_key_data(evt)) { + wxGetApp().plater()->get_current_canvas3D()->render(); + return true; + } + return false; + }; #ifndef __APPLE__ // BBS undo - append_menu_item(editMenu, wxID_ANY, _L("Undo") + "\tCtrl+Z", + append_menu_item(editMenu, wxID_ANY, _L("Undo") + "\t" + ctrl + "Z", _L("Undo"), [this](wxCommandEvent&) { m_plater->undo(); }, "menu_undo", nullptr, [this](){return m_plater->can_undo(); }, this); // BBS redo - append_menu_item(editMenu, wxID_ANY, _L("Redo") + "\tCtrl+Y", + append_menu_item(editMenu, wxID_ANY, _L("Redo") + "\t" + ctrl + "Y", _L("Redo"), [this](wxCommandEvent&) { m_plater->redo(); }, "menu_redo", nullptr, [this](){return m_plater->can_redo(); }, this); editMenu->AppendSeparator(); // BBS Cut TODO - append_menu_item(editMenu, wxID_ANY, _L("Cut") + "\tCtrl+X", + append_menu_item(editMenu, wxID_ANY, _L("Cut") + "\t" + ctrl + "X", _L("Cut selection to clipboard"), [this](wxCommandEvent&) {m_plater->cut_selection_to_clipboard(); }, "menu_cut", nullptr, [this]() {return m_plater->can_copy_to_clipboard(); }, this); // BBS Copy - append_menu_item(editMenu, wxID_ANY, _L("Copy") + "\tCtrl+C", + append_menu_item(editMenu, wxID_ANY, _L("Copy") + "\t" + ctrl + "C", _L("Copy selection to clipboard"), [this](wxCommandEvent&) { m_plater->copy_selection_to_clipboard(); }, "menu_copy", nullptr, [this](){return m_plater->can_copy_to_clipboard(); }, this); // BBS Paste - append_menu_item(editMenu, wxID_ANY, _L("Paste") + "\tCtrl+V", + append_menu_item(editMenu, wxID_ANY, _L("Paste") + "\t" + ctrl + "V", _L("Paste clipboard"), [this](wxCommandEvent&) { m_plater->paste_from_clipboard(); }, "menu_paste", nullptr, [this](){return m_plater->can_paste_from_clipboard(); }, this); // BBS Delete selected - append_menu_item(editMenu, wxID_ANY, _L("Delete selected") + "\tDel", + append_menu_item(editMenu, wxID_ANY, _L("Delete selected") + "\t" + _L("Del"), _L("Deletes the current selection"),[this](wxCommandEvent&) { m_plater->remove_selected(); }, "menu_remove", nullptr, [this](){return can_delete(); }, this); //BBS: delete all - append_menu_item(editMenu, wxID_ANY, _L("Delete all") + "\tCtrl+D", + append_menu_item(editMenu, wxID_ANY, _L("Delete all") + "\t" + ctrl + "D", _L("Deletes all objects"),[this](wxCommandEvent&) { m_plater->delete_all_objects_from_model(); }, "menu_remove", nullptr, [this](){return can_delete_all(); }, this); editMenu->AppendSeparator(); @@ -2220,40 +2243,97 @@ void MainFrame::init_menubar_as_editor() editMenu->AppendSeparator(); #else // BBS undo - append_menu_item(editMenu, wxID_ANY, _L("Undo") + "\tCtrl+Z", - _L("Undo"), [this](wxCommandEvent&) { m_plater->undo(); }, + append_menu_item(editMenu, wxID_ANY, _L("Undo") + "\t" + ctrl + "Z", + _L("Undo"), [this, handle_key_event](wxCommandEvent&) { + wxKeyEvent e; + e.SetEventType(wxEVT_KEY_DOWN); + e.SetControlDown(true); + e.m_keyCode = 'Z'; + if (handle_key_event(e)) { + return; + } + m_plater->undo(); }, "", nullptr, [this](){return m_plater->can_undo(); }, this); // BBS redo - append_menu_item(editMenu, wxID_ANY, _L("Redo") + "\tCtrl+Y", - _L("Redo"), [this](wxCommandEvent&) { m_plater->redo(); }, + append_menu_item(editMenu, wxID_ANY, _L("Redo") + "\t" + ctrl + "Y", + _L("Redo"), [this, handle_key_event](wxCommandEvent&) { + wxKeyEvent e; + e.SetEventType(wxEVT_KEY_DOWN); + e.SetControlDown(true); + e.m_keyCode = 'Y'; + if (handle_key_event(e)) { + return; + } + m_plater->redo(); }, "", nullptr, [this](){return m_plater->can_redo(); }, this); editMenu->AppendSeparator(); // BBS Cut TODO - append_menu_item(editMenu, wxID_ANY, _L("Cut") + "\tCtrl+X", - _L("Cut selection to clipboard"), [this](wxCommandEvent&) {m_plater->cut_selection_to_clipboard(); }, + append_menu_item(editMenu, wxID_ANY, _L("Cut") + "\t" + ctrl + "X", + _L("Cut selection to clipboard"), [this, handle_key_event](wxCommandEvent&) { + wxKeyEvent e; + e.SetEventType(wxEVT_KEY_DOWN); + e.SetControlDown(true); + e.m_keyCode = 'X'; + if (handle_key_event(e)) { + return; + } + m_plater->cut_selection_to_clipboard(); }, "", nullptr, [this]() {return m_plater->can_copy_to_clipboard(); }, this); // BBS Copy - append_menu_item(editMenu, wxID_ANY, _L("Copy") + "\tCtrl+C", - _L("Copy selection to clipboard"), [this](wxCommandEvent&) { m_plater->copy_selection_to_clipboard(); }, + append_menu_item(editMenu, wxID_ANY, _L("Copy") + "\t" + ctrl + "C", + _L("Copy selection to clipboard"), [this, handle_key_event](wxCommandEvent&) { + wxKeyEvent e; + e.SetEventType(wxEVT_KEY_DOWN); + e.SetControlDown(true); + e.m_keyCode = 'C'; + if (handle_key_event(e)) { + return; + } + m_plater->copy_selection_to_clipboard(); }, "", nullptr, [this](){return m_plater->can_copy_to_clipboard(); }, this); // BBS Paste - append_menu_item(editMenu, wxID_ANY, _L("Paste") + "\tCtrl+V", - _L("Paste clipboard"), [this](wxCommandEvent&) { m_plater->paste_from_clipboard(); }, + append_menu_item(editMenu, wxID_ANY, _L("Paste") + "\t" + ctrl + "V", + _L("Paste clipboard"), [this, handle_key_event](wxCommandEvent&) { + wxKeyEvent e; + e.SetEventType(wxEVT_KEY_DOWN); + e.SetControlDown(true); + e.m_keyCode = 'V'; + if (handle_key_event(e)) { + return; + } + m_plater->paste_from_clipboard(); }, "", nullptr, [this](){return m_plater->can_paste_from_clipboard(); }, this); #if 0 // BBS Delete selected append_menu_item(editMenu, wxID_ANY, _L("Delete selected") + "\tBackSpace", - _L("Deletes the current selection"),[this](wxCommandEvent&) { m_plater->remove_selected(); }, + _L("Deletes the current selection"),[this](wxCommandEvent&) { + m_plater->remove_selected(); + }, "", nullptr, [this](){return can_delete(); }, this); #endif //BBS: delete all - append_menu_item(editMenu, wxID_ANY, _L("Delete all") + "\tCtrl+D", - _L("Deletes all objects"),[this](wxCommandEvent&) { m_plater->delete_all_objects_from_model(); }, + append_menu_item(editMenu, wxID_ANY, _L("Delete all") + "\t" + ctrl + "D", + _L("Deletes all objects"),[this, handle_key_event](wxCommandEvent&) { + wxKeyEvent e; + e.SetEventType(wxEVT_KEY_DOWN); + e.SetControlDown(true); + e.m_keyCode = 'D'; + if (handle_key_event(e)) { + return; + } + m_plater->delete_all_objects_from_model(); }, "", nullptr, [this](){return can_delete_all(); }, this); editMenu->AppendSeparator(); // BBS Clone Selected - append_menu_item(editMenu, wxID_ANY, _L("Clone selected") + "\tCtrl+M", - _L("Clone copies of selections"),[this](wxCommandEvent&) { + append_menu_item(editMenu, wxID_ANY, _L("Clone selected") + "\t" + ctrl + "M", + _L("Clone copies of selections"),[this, handle_key_event](wxCommandEvent&) { + wxKeyEvent e; + e.SetEventType(wxEVT_KEY_DOWN); + e.SetControlDown(true); + e.m_keyCode = 'M'; + if (handle_key_event(e)) { + return; + } m_plater->clone_selection(); }, "", nullptr, [this](){return can_clone(); }, this); @@ -2261,12 +2341,27 @@ void MainFrame::init_menubar_as_editor() #endif // BBS Select All - append_menu_item(editMenu, wxID_ANY, _L("Select all") + "\tCtrl+A", - _L("Selects all objects"), [this](wxCommandEvent&) { m_plater->select_all(); }, + append_menu_item(editMenu, wxID_ANY, _L("Select all") + "\t" + ctrl + "A", + _L("Selects all objects"), [this, handle_key_event](wxCommandEvent&) { + wxKeyEvent e; + e.SetEventType(wxEVT_KEY_DOWN); + e.SetControlDown(true); + e.m_keyCode = 'A'; + if (handle_key_event(e)) { + return; + } + m_plater->select_all(); }, "", nullptr, [this](){return can_select(); }, this); // BBS Deslect All append_menu_item(editMenu, wxID_ANY, _L("Deselect all") + "\tEsc", - _L("Deselects all objects"), [this](wxCommandEvent&) { m_plater->deselect_all(); }, + _L("Deselects all objects"), [this, handle_key_event](wxCommandEvent&) { + wxKeyEvent e; + e.SetEventType(wxEVT_KEY_DOWN); + e.m_keyCode = WXK_ESCAPE; + if (handle_key_event(e)) { + return; + } + m_plater->deselect_all(); }, "", nullptr, [this](){return can_deselect(); }, this); //editMenu->AppendSeparator(); //append_menu_check_item(editMenu, wxID_ANY, _L("Show Model Mesh(TODO)"), @@ -2320,7 +2415,7 @@ void MainFrame::init_menubar_as_editor() viewMenu->Check(wxID_CAMERA_ORTHOGONAL + camera_id_base, true); viewMenu->AppendSeparator(); - append_menu_check_item(viewMenu, wxID_ANY, _L("Show &Labels") + "\tCtrl+E", _L("Show object labels in 3D scene"), + append_menu_check_item(viewMenu, wxID_ANY, _L("Show &Labels") + "\t" + ctrl + "E", _L("Show object labels in 3D scene"), [this](wxCommandEvent&) { m_plater->show_view3D_labels(!m_plater->are_view3D_labels_shown()); m_plater->get_current_canvas3D()->post_event(SimpleEvent(wxEVT_PAINT)); }, this, [this]() { return m_plater->is_view3D_shown(); }, [this]() { return m_plater->are_view3D_labels_shown(); }, this); @@ -2351,7 +2446,7 @@ void MainFrame::init_menubar_as_editor() //auto preference_item = new wxMenuItem(parent_menu, BambuStudioMenuPreferences + bambu_studio_id_base, _L("Preferences") + "\tCtrl+,", ""); #else wxMenu* parent_menu = m_topbar->GetTopMenu(); - auto preference_item = new wxMenuItem(parent_menu, ConfigMenuPreferences + config_id_base, _L("Preferences") + "\tCtrl+P", ""); + auto preference_item = new wxMenuItem(parent_menu, ConfigMenuPreferences + config_id_base, _L("Preferences") + "\t" + ctrl + "P", ""); #endif //auto printer_item = new wxMenuItem(parent_menu, ConfigMenuPrinter + config_id_base, _L("Printer"), ""); @@ -2393,7 +2488,7 @@ void MainFrame::init_menubar_as_editor() // } // case ConfigMenuPreferences: // { -// wxGetApp().CallAfter([this] { +// CallAfter([this] { // PreferencesDialog dlg(this); // dlg.ShowModal(); //#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER @@ -2430,7 +2525,7 @@ void MainFrame::init_menubar_as_editor() // Slic3r::GUI::about(); // break; // case BambuStudioMenuPreferences: - // wxGetApp().CallAfter([this] { + // CallAfter([this] { // PreferencesDialog dlg(this); // dlg.ShowModal(); //#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER @@ -2451,7 +2546,7 @@ void MainFrame::init_menubar_as_editor() [this](wxCommandEvent &) { Slic3r::GUI::about();}, "", nullptr, []() { return true; }, this, 0); append_menu_item( - parent_menu, wxID_ANY, _L("Preferences") + "\tCtrl+,", "", + parent_menu, wxID_ANY, _L("Preferences") + "\t" + ctrl + ",", "", [this](wxCommandEvent &) { PreferencesDialog dlg(this); dlg.ShowModal(); @@ -2478,7 +2573,7 @@ void MainFrame::init_menubar_as_editor() //BBS add Preference append_menu_item( - m_topbar->GetTopMenu(), wxID_ANY, _L("Preferences") + "\tCtrl+P", "", + m_topbar->GetTopMenu(), wxID_ANY, _L("Preferences") + "\t" + ctrl + "P", "", [this](wxCommandEvent &) { PreferencesDialog dlg(this); dlg.ShowModal(); @@ -2654,6 +2749,23 @@ void MainFrame::show_publish_button(bool show) } } +void MainFrame::set_max_recent_count(int max) +{ + max = max < 0 ? 0 : max > 10000 ? 10000 : max; + size_t count = m_recent_projects.GetCount(); + m_recent_projects.SetMaxFiles(max); + if (count != m_recent_projects.GetCount()) { + count = m_recent_projects.GetCount(); + std::vector recent_projects; + for (size_t i = 0; i < count; ++i) { + recent_projects.push_back(into_u8(m_recent_projects.GetHistoryFile(i))); + } + wxGetApp().app_config->set_recent_projects(recent_projects); + wxGetApp().app_config->save(); + m_webview->SendRecentList(""); + } +} + void MainFrame::open_menubar_item(const wxString& menu_name,const wxString& item_name) { if (m_menubar == nullptr) @@ -3163,6 +3275,14 @@ void MainFrame::FileHistory::LoadThumbnails() m_load_called = true; } +inline void MainFrame::FileHistory::SetMaxFiles(int max) +{ + m_fileMaxFiles = max; + size_t numFiles = m_fileHistory.size(); + while (numFiles > m_fileMaxFiles) + RemoveFileFromHistory(--numFiles); +} + void MainFrame::get_recent_projects(boost::property_tree::wptree &tree) { for (size_t i = 0; i < m_recent_projects.GetCount(); ++i) { @@ -3190,7 +3310,7 @@ void MainFrame::open_recent_project(size_t file_id, wxString const & filename) file_id = m_recent_projects.FindFileInHistory(filename); } if (wxFileExists(filename)) { - wxGetApp().CallAfter([this, filename] { + CallAfter([this, filename] { if (wxGetApp().can_load_project()) m_plater->load_project(filename); }); diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 59c8c7f355..0e1223a196 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -20,6 +20,7 @@ #include "ParamsPanel.hpp" #include "Monitor.hpp" #include "Auxiliary.hpp" +#include "Project.hpp" #include "UnsavedChangesDialog.hpp" #include "Widgets/SideButton.hpp" #include "Widgets/SideMenuPopup.hpp" @@ -156,6 +157,8 @@ class MainFrame : public DPIFrame size_t FindFileInHistory(const wxString &file); void LoadThumbnails(); + + void SetMaxFiles(int max); private: std::deque m_thumbnails; bool m_load_called = false; @@ -245,6 +248,7 @@ public: void update_title(); void show_publish_button(bool show); + void set_max_recent_count(int max); void update_title_colour_after_set_title(); void show_option(bool show); @@ -344,7 +348,10 @@ public: Plater* m_plater { nullptr }; //BBS: GUI refactor MonitorPanel* m_monitor{ nullptr }; - AuxiliaryPanel* m_auxiliary{ nullptr }; + + //AuxiliaryPanel* m_auxiliary{ nullptr }; + ProjectPanel* m_project{ nullptr }; + WebViewPanel* m_webview { nullptr }; PrinterWebView* m_printer_view{nullptr}; wxLogWindow* m_log_window { nullptr }; diff --git a/src/slic3r/GUI/MediaFilePanel.cpp b/src/slic3r/GUI/MediaFilePanel.cpp index 4a6e684943..20e91953b2 100644 --- a/src/slic3r/GUI/MediaFilePanel.cpp +++ b/src/slic3r/GUI/MediaFilePanel.cpp @@ -31,9 +31,9 @@ MediaFilePanel::MediaFilePanel(wxWindow * parent) m_button_year = new ::Button(m_time_panel, _L("Year"), "", wxBORDER_NONE); m_button_month = new ::Button(m_time_panel, _L("Month"), "", wxBORDER_NONE); m_button_all = new ::Button(m_time_panel, _L("All Files"), "", wxBORDER_NONE); - m_button_year->SetToolTip(L("Group files by year, recent first.")); - m_button_month->SetToolTip(L("Group files by month, recent first.")); - m_button_all->SetToolTip(L("Show all files, recent first.")); + m_button_year->SetToolTip(_L("Group files by year, recent first.")); + m_button_month->SetToolTip(_L("Group files by month, recent first.")); + m_button_all->SetToolTip(_L("Show all files, recent first.")); m_button_all->SetFont(Label::Head_14); // sync with m_last_mode for (auto b : {m_button_year, m_button_month, m_button_all}) { b->SetBackgroundColor(StateColor()); @@ -228,14 +228,15 @@ void MediaFilePanel::SetMachineObject(MachineObject* obj) m_button_download->Enable(e.GetInt() > 0); }); fs->Bind(EVT_MODE_CHANGED, &MediaFilePanel::modeChanged, this); - fs->Bind(EVT_STATUS_CHANGED, [this, wfs = boost::weak_ptr(fs)](auto &e) { + fs->Bind(EVT_STATUS_CHANGED, [this, wfs = boost::weak_ptr(fs)](auto& e) { e.Skip(); boost::shared_ptr fs(wfs.lock()); if (m_image_grid->GetFileSystem() != fs) // canceled return; ScalableBitmap icon; wxString msg; - switch (e.GetInt()) { + int status = e.GetInt(); + switch (status) { case PrinterFileSystem::Initializing: icon = m_bmp_loading; msg = _L("Initializing..."); break; case PrinterFileSystem::Connecting: icon = m_bmp_loading; msg = _L("Connecting..."); break; case PrinterFileSystem::Failed: icon = m_bmp_failed; msg = _L("Connect failed [%d]!"); break; @@ -246,6 +247,53 @@ void MediaFilePanel::SetMachineObject(MachineObject* obj) m_image_grid->SetStatus(icon, msg); if (e.GetInt() == PrinterFileSystem::Initializing) fetchUrl(boost::weak_ptr(fs)); + + if (status == PrinterFileSystem::Failed + || status == PrinterFileSystem::ListReady) { + json j; + j["code"] = fs->GetLastError(); + j["dev_id"] = m_machine; + j["dev_ip"] = m_lan_ip; + NetworkAgent* agent = wxGetApp().getAgent(); + if (status == PrinterFileSystem::Failed) { + j["result"] = "failed"; + if (agent) + agent->track_event("download_video_conn", j.dump()); + } else if (status == PrinterFileSystem::ListReady) { + j["result"] = "success"; + if (agent) + agent->track_event("download_video_conn", j.dump()); + } + } + }); + fs->Bind(EVT_DOWNLOAD, [this, wfs = boost::weak_ptr(fs)](auto& e) { + e.Skip(); + boost::shared_ptr fs(wfs.lock()); + if (m_image_grid->GetFileSystem() != fs) // canceled + return; + + int result = e.GetExtraLong(); + NetworkAgent* agent = wxGetApp().getAgent(); + if (result > 1 || result == 0) { + json j; + j["code"] = result; + j["dev_id"] = m_machine; + j["dev_ip"] = m_lan_ip; + if (result > 1) { + // download failed + j["result"] = "failed"; + if (agent) { + agent->track_event("download_video", j.dump()); + } + } else if (result == 0) { + // download success + j["result"] = "success"; + if (agent) { + agent->track_event("download_video", j.dump()); + } + } + } + return; }); if (IsShown()) fs->Start(); } diff --git a/src/slic3r/GUI/MediaPlayCtrl.cpp b/src/slic3r/GUI/MediaPlayCtrl.cpp index 2e88131df3..f639d6e647 100644 --- a/src/slic3r/GUI/MediaPlayCtrl.cpp +++ b/src/slic3r/GUI/MediaPlayCtrl.cpp @@ -27,9 +27,6 @@ MediaPlayCtrl::MediaPlayCtrl(wxWindow *parent, wxMediaCtrl2 *media_ctrl, const w { SetBackgroundColour(*wxWHITE); m_media_ctrl->Bind(wxEVT_MEDIA_STATECHANGED, &MediaPlayCtrl::onStateChanged, this); -#if wxUSE_GSTREAMER_PLAYER - m_media_ctrl->Bind(wxEVT_MEDIA_LOADED, &MediaPlayCtrl::onStateChanged, this); -#endif m_button_play = new Button(this, "", "media_play", wxBORDER_NONE); m_button_play->SetCanFocus(false); @@ -195,8 +192,8 @@ void MediaPlayCtrl::Play() m_url = url; if (m_last_state == MEDIASTATE_INITIALIZING) { if (url.empty() || !boost::algorithm::starts_with(url, "bambu:///")) { - Stop(); - SetStatus(wxString::Format(_L("Initialize failed (%s)!"), url.empty() ? _L("Network unreachable") : from_u8(url))); + m_failed_code = 3; + Stop(wxString::Format(_L("Initialize failed (%s)!"), url.empty() ? _L("Network unreachable") : from_u8(url))); } else { m_last_state = MEDIASTATE_LOADING; SetStatus(_L("Loading...")); @@ -218,6 +215,8 @@ void MediaPlayCtrl::Play() void MediaPlayCtrl::Stop(wxString const &msg) { + bool init_failed = m_last_state != wxMEDIASTATE_PLAYING; + if (m_last_state != MEDIASTATE_IDLE) { m_media_ctrl->InvalidateBestSize(); m_button_play->SetIcon("media_play"); @@ -235,13 +234,31 @@ void MediaPlayCtrl::Stop(wxString const &msg) m_next_retry = wxDateTime(); } else if (!msg.IsEmpty()) { SetStatus(msg, false); + } else { + m_failed_code = 0; } + + if (init_failed && m_failed_code != 0 && m_last_failed_code != m_failed_code) { + json j; + j["stage"] = std::to_string(m_last_state); + j["dev_id"] = m_machine; + j["dev_ip"] = m_lan_ip; + j["result"] = "failed"; + j["code"] = m_failed_code; + j["msg"] = into_u8(msg); + NetworkAgent *agent = wxGetApp().getAgent(); + if (agent) + agent->track_event("start_liveview", j.dump()); + } + m_last_failed_code = m_failed_code; + ++m_failed_retry; if (m_failed_code != 0 && !m_tutk_support && (m_failed_retry > 1 || m_user_triggered)) { m_next_retry = wxDateTime(); // stop retry if (wxGetApp().show_modal_ip_address_enter_dialog(_L("LAN Connection Failed (Failed to start liveview)"))) { m_failed_retry = 0; m_user_triggered = true; + m_last_failed_code = 0; m_next_retry = wxDateTime::Now(); return; } @@ -259,6 +276,7 @@ void MediaPlayCtrl::TogglePlay() } else { m_failed_retry = 0; m_user_triggered = true; + m_last_failed_code = 0; m_next_retry = wxDateTime::Now(); Play(); } @@ -301,7 +319,7 @@ void MediaPlayCtrl::ToggleStream() { return std::make_shared(pri); } void on_finish() override { - wxGetApp().CallAfter([ctrl = this->ctrl] { ctrl->ToggleStream(); }); + ctrl->CallAfter([ctrl = this->ctrl] { ctrl->ToggleStream(); }); EndModal(wxID_CLOSE); } }; @@ -365,23 +383,34 @@ void MediaPlayCtrl::onStateChanged(wxMediaEvent &event) Stop(); return; } - if (last_state == MEDIASTATE_LOADING && (state == wxMEDIASTATE_STOPPED || state == wxMEDIASTATE_PAUSED || event.GetEventType() == wxEVT_MEDIA_LOADED)) { + if (last_state == MEDIASTATE_LOADING && (state == wxMEDIASTATE_STOPPED || state == wxMEDIASTATE_PAUSED)) { wxSize size = m_media_ctrl->GetVideoSize(); BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl::onStateChanged: size: " << size.x << "x" << size.y; m_failed_code = m_media_ctrl->GetLastError(); - if (size.GetWidth() > 1000 || (event.GetEventType() == wxEVT_MEDIA_LOADED)) { + if (size.GetWidth() > 1000) { m_last_state = state; SetStatus(_L("Playing..."), false); + + // track event + json j; + j["stage"] = std::to_string(m_last_state); + j["dev_id"] = m_machine; + j["dev_ip"] = m_lan_ip; + j["result"] = "success"; + j["code"] = 0; + NetworkAgent* agent = wxGetApp().getAgent(); + if (agent) + agent->track_event("start_liveview", j.dump()); + m_failed_retry = 0; m_failed_code = 0; boost::unique_lock lock(m_mutex); m_tasks.push_back(""); m_cond.notify_all(); } else if (event.GetId()) { - Stop(); if (m_failed_code == 0) m_failed_code = 2; - SetStatus(_L("Load failed [%d]!")); + Stop(_L("Load failed [%d]!")); } } else { m_last_state = state; @@ -563,7 +592,7 @@ void wxMediaCtrl2::DoSetSize(int x, int y, int width, int height, int sizeFlags) if (maxHeight != GetMaxHeight()) { // BOOST_LOG_TRIVIAL(info) << "wxMediaCtrl2::DoSetSize: width: " << width << ", height: " << height << ", maxHeight: " << maxHeight; SetMaxSize({-1, maxHeight}); - Slic3r::GUI::wxGetApp().CallAfter([this] { + CallAfter([this] { if (auto p = GetParent()) { p->Layout(); p->Refresh(); diff --git a/src/slic3r/GUI/MediaPlayCtrl.h b/src/slic3r/GUI/MediaPlayCtrl.h index f3ab91f161..05bc6d9ef0 100644 --- a/src/slic3r/GUI/MediaPlayCtrl.h +++ b/src/slic3r/GUI/MediaPlayCtrl.h @@ -86,6 +86,7 @@ private: bool m_user_triggered = false; int m_failed_retry = 0; int m_failed_code = 0; + int m_last_failed_code = 0; wxDateTime m_next_retry; ::Button *m_button_play; diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 440b0ec0db..6fd3a54b8e 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -11,6 +11,7 @@ #include #include +#include "CameraUtils.hpp" namespace Slic3r { @@ -75,7 +76,24 @@ void MeshClipper::render_cut() m_vertex_array.render(); } +bool MeshClipper::is_projection_inside_cut(const Vec3d &point_in) const +{ + if (!m_result || m_result->cut_islands.empty()) + return false; + Vec3d point = m_result->trafo.inverse() * point_in; + Point pt_2d = Point::new_scale(Vec2d(point.x(), point.y())); + for (const CutIsland &isl : m_result->cut_islands) { + if (isl.expoly_bb.contains(pt_2d) && isl.expoly.contains(pt_2d)) + return true; + } + return false; +} + +bool MeshClipper::has_valid_contour() const +{ + return m_result && std::any_of(m_result->cut_islands.begin(), m_result->cut_islands.end(), [](const CutIsland &isl) { return !isl.expoly.empty(); }); +} void MeshClipper::recalculate_triangles() { @@ -104,6 +122,9 @@ void MeshClipper::recalculate_triangles() tr.rotate(q); tr = m_trafo.get_matrix() * tr; + m_result = ClipResult(); + m_result->trafo = tr; + if (m_limiting_plane != ClippingPlane::ClipsNothing()) { // Now remove whatever ended up below the limiting plane (e.g. sinking objects). @@ -157,6 +178,13 @@ void MeshClipper::recalculate_triangles() } } + for (const ExPolygon &exp : expolys) { + m_result->cut_islands.push_back(CutIsland()); + CutIsland &isl = m_result->cut_islands.back(); + isl.expoly = std::move(exp); + isl.expoly_bb = get_extents(exp); + } + m_triangles2d = triangulate_expolygons_2f(expolys, m_trafo.get_matrix().matrix().determinant() < 0.); tr.pretranslate(0.001 * m_plane.get_normal().normalized()); // to avoid z-fighting @@ -180,6 +208,14 @@ Vec3f MeshRaycaster::get_triangle_normal(size_t facet_idx) const return m_normals[facet_idx]; } +void MeshRaycaster::line_from_mouse_pos_static(const Vec2d &mouse_pos, const Transform3d &trafo, const Camera &camera, Vec3d &point, Vec3d &direction) +{ + CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction); + Transform3d inv = trafo.inverse(); + point = inv * point; + direction = inv.linear() * direction; +} + void MeshRaycaster::line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, Vec3d& point, Vec3d& direction) const { diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index bb8a1aa618..02c597da3c 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -94,6 +94,9 @@ public: // be set in world coords. void render_cut(); + bool is_projection_inside_cut(const Vec3d &point) const; + bool has_valid_contour() const; + private: void recalculate_triangles(); @@ -105,6 +108,18 @@ private: std::vector m_triangles2d; GLIndexedVertexArray m_vertex_array; bool m_triangles_valid = false; + + struct CutIsland + { + ExPolygon expoly; + BoundingBox expoly_bb; + }; + struct ClipResult + { + std::vector cut_islands; + Transform3d trafo; // this rotates the cut into world coords + }; + std::optional m_result; // the cut plane }; @@ -121,6 +136,9 @@ public: { } + static void line_from_mouse_pos_static(const Vec2d &mouse_pos, const Transform3d &trafo, + const Camera &camera, Vec3d &point, Vec3d &direction); + void line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, Vec3d& point, Vec3d& direction) const; diff --git a/src/slic3r/GUI/ModelMall.cpp b/src/slic3r/GUI/ModelMall.cpp index bcc0a7e554..8fabd073e4 100644 --- a/src/slic3r/GUI/ModelMall.cpp +++ b/src/slic3r/GUI/ModelMall.cpp @@ -127,32 +127,8 @@ namespace GUI { json j = json::parse(strInput); wxString strCmd = j["command"]; - - if (strCmd == "request_model_download") { - - std::string model_id = ""; - if (j["data"].contains("download_url")) - model_id = j["data"]["model_id"].get(); - - std::string profile_id = ""; - if (j["data"].contains("profile_id")) - profile_id = j["data"]["profile_id"].get(); - - std::string download_url = ""; - if (j["data"].contains("download_url")) - download_url = j["data"]["download_url"].get(); - - std::string filename = ""; - if (j["data"].contains("filename")) - filename = j["data"]["filename"].get(); - - if (download_url.empty()) return; - - wxGetApp().set_download_model_url(download_url); - wxGetApp().set_download_model_name(filename); - wxGetApp().plater()->request_model_download(); - } - else if(strCmd == "request_close_publish_window") { + + if(strCmd == "request_close_publish_window") { this->Hide(); } diff --git a/src/slic3r/GUI/Monitor.cpp b/src/slic3r/GUI/Monitor.cpp index c2f5fe086d..4056dbb33b 100644 --- a/src/slic3r/GUI/Monitor.cpp +++ b/src/slic3r/GUI/Monitor.cpp @@ -514,6 +514,24 @@ void MonitorPanel::show_status(int status) if (last_status == status) return; + + if (last_status & (int)MonitorStatus::MONITOR_CONNECTING != 0) { + NetworkAgent* agent = wxGetApp().getAgent(); + json j; + j["dev_id"] = obj ? obj->dev_id : "obj_nullptr"; + if (status & (int)MonitorStatus::MONITOR_DISCONNECTED != 0) { + j["result"] = "failed"; + if (agent) { + agent->track_event("connect_dev", j.dump()); + } + } + else if (status & (int)MonitorStatus::MONITOR_NORMAL != 0) { + j["result"] = "success"; + if (agent) { + agent->track_event("connect_dev", j.dump()); + } + } + } last_status = status; BOOST_LOG_TRIVIAL(info) << "monitor: show_status = " << status; diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp index b415195297..6e705f4c06 100644 --- a/src/slic3r/GUI/MsgDialog.cpp +++ b/src/slic3r/GUI/MsgDialog.cpp @@ -74,7 +74,7 @@ MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &he for (auto mb : m_buttons) { delete mb.second->buttondata ; delete mb.second; } } -void MsgDialog::show_dsa_button() +void MsgDialog::show_dsa_button(wxString const &title) { m_checkbox_dsa = new CheckBox(this); m_dsa_sizer->Add(m_checkbox_dsa, 0, wxALL | wxALIGN_CENTER, FromDIP(2)); @@ -86,7 +86,7 @@ void MsgDialog::show_dsa_button() e.Skip(); }); - auto m_text_dsa = new wxStaticText(this, wxID_ANY, _L("Don't show again"), wxDefaultPosition, wxDefaultSize, 0); + auto m_text_dsa = new wxStaticText(this, wxID_ANY, title.IsEmpty() ? _L("Don't show again") : title, wxDefaultPosition, wxDefaultSize, 0); m_dsa_sizer->Add(m_text_dsa, 0, wxALL | wxALIGN_CENTER, FromDIP(2)); m_text_dsa->SetFont(::Label::Body_13); m_text_dsa->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#323A3D"))); @@ -136,7 +136,7 @@ void MsgDialog::SetButtonLabel(wxWindowID btn_id, const wxString& label, bool se Button* MsgDialog::add_button(wxWindowID btn_id, bool set_focus /*= false*/, const wxString& label/* = wxString()*/) { - Button* btn = new Button(this, label); + Button* btn = new Button(this, label, "", 0, 0, btn_id); ButtonSizeType type; if (label.length() < 5) { diff --git a/src/slic3r/GUI/MsgDialog.hpp b/src/slic3r/GUI/MsgDialog.hpp index d0e3bfaca9..c9b0ded3c5 100644 --- a/src/slic3r/GUI/MsgDialog.hpp +++ b/src/slic3r/GUI/MsgDialog.hpp @@ -61,7 +61,7 @@ struct MsgDialog : DPIDialog MsgDialog &operator=(const MsgDialog &) = delete; virtual ~MsgDialog(); - void show_dsa_button(); + void show_dsa_button(wxString const & title = {}); bool get_checkbox_state(); virtual void on_dpi_changed(const wxRect& suggested_rect); void SetButtonLabel(wxWindowID btn_id, const wxString& label, bool set_focus = false); diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 11e87e4d47..9588a293e6 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -861,7 +861,7 @@ void NotificationManager::ExportFinishedNotification::render_eject_button(ImGuiW ImGui::PushStyleColor(ImGuiCol_Border, { 0,0,0,0 }); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 8 * scale, 1 * scale }); ImGui::BeginTooltip(); - imgui.text(_u8L("Safely remove hardware.")); + imgui.text(_L("Safely remove hardware.")); ImGui::EndTooltip(); ImGui::PopStyleColor(2); ImGui::PopStyleVar(); @@ -1020,6 +1020,7 @@ void NotificationManager::UpdatedItemsInfoNotification::add_type(InfoItemType ty case InfoItemType::MmuSegmentation: text += format(_L_PLURAL("%1$d Object has color painting.", "%1$d Objects have color painting.",(*it).second), (*it).second) + "\n"; break; // BBS //case InfoItemType::Sinking: text += format(("%1$d Object has partial sinking.", "%1$d Objects have partial sinking.", (*it).second), (*it).second) + "\n"; break; + case InfoItemType::CutConnectors: text += format(_L_PLURAL("%1$d object was loaded as a part of cut object.", "%1$d objects were loaded as parts of cut object", (*it).second), (*it).second) + "\n"; break; default: BOOST_LOG_TRIVIAL(error) << "Unknown InfoItemType: " << (*it).second; break; } } @@ -1111,13 +1112,14 @@ void NotificationManager::PrintHostUploadNotification::render_bar(ImGuiWrapper& } void NotificationManager::PrintHostUploadNotification::render_left_sign(ImGuiWrapper& imgui) { - if (m_uj_state == UploadJobState::PB_ERROR) { - std::string text; - text = ImGui::ErrorMarker; - ImGui::SetCursorPosX(m_line_height / 3); - ImGui::SetCursorPosY(m_window_height / 2 - m_line_height); - imgui.text(text.c_str()); - } + // BBS: do not render left button + //if (m_uj_state == UploadJobState::PB_ERROR) { + // std::string text; + // text = ImGui::ErrorMarker; + // ImGui::SetCursorPosX(m_line_height / 3); + // ImGui::SetCursorPosY(m_window_height / 2 - m_line_height); + // imgui.text(text.c_str()); + //} } void NotificationManager::PrintHostUploadNotification::render_cancel_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y) { diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index 9df38a4b83..aead8232d8 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -37,6 +37,7 @@ static constexpr char LayerRootIcon[] = "blank"; static constexpr char LayerIcon[] = "blank"; static constexpr char WarningIcon[] = "obj_warning"; static constexpr char WarningManifoldIcon[] = "obj_warning"; +static constexpr char LockIcon[] = "cut_"; ObjectDataViewModelNode::ObjectDataViewModelNode(PartPlate* part_plate, wxString name) : m_parent(nullptr), @@ -65,6 +66,7 @@ const std::map INFO_ITEMS{ //{ InfoItemType::CustomSeam, {L("Paint-on seam"), "seam_" }, }, { InfoItemType::MmuSegmentation, {L("Color painting"), "mmu_segmentation"}, }, //{ InfoItemType::Sinking, {L("Sinking"), "objlist_sinking"}, }, + { InfoItemType::CutConnectors, {L("Cut connectors"), "cut_connectors" }, }, }; ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent, @@ -222,6 +224,15 @@ void ObjectDataViewModelNode::set_support_icon(bool enable) m_support_icon = create_scaled_bitmap("dot"); } +void ObjectDataViewModelNode::set_sinking_icon(bool enable) +{ + m_sink_enable = enable; + if ((m_type & itObject) && enable) + m_sinking_icon = create_scaled_bitmap("objlist_sinking"); + else + m_sinking_icon = create_scaled_bitmap("dot"); +} + void ObjectDataViewModelNode::set_warning_icon(const std::string& warning_icon_name) { m_warning_icon_name = warning_icon_name; @@ -302,6 +313,9 @@ bool ObjectDataViewModelNode::SetValue(const wxVariant& variant, unsigned col) case colSupportPaint: m_support_icon << variant; break; + case colSinking: + m_sinking_icon << variant; + break; case colColorPaint: m_color_icon << variant; break; @@ -399,6 +413,7 @@ ObjectDataViewModel::ObjectDataViewModel() m_volume_bmps = MenuFactory::get_volume_bitmaps(); m_warning_bmp = create_scaled_bitmap(WarningIcon); m_warning_manifold_bmp = create_scaled_bitmap(WarningManifoldIcon); + m_lock_bmp = create_scaled_bitmap(LockIcon); for (auto item : INFO_ITEMS) m_info_bmps[item.first] = create_scaled_bitmap(item.second.bmp_name); @@ -478,7 +493,47 @@ wxDataViewItem ObjectDataViewModel::AddOutsidePlate(bool refresh) return plate_item; } -wxDataViewItem ObjectDataViewModel::AddObject(ModelObject* model_object, std::string warning_bitmap, bool refresh) +void ObjectDataViewModel::UpdateBitmapForNode(ObjectDataViewModelNode *node) +{ + bool is_volume_node = node->GetType() & itVolume; + int vol_type = static_cast(node->GetVolumeType()); + is_volume_node &= (vol_type >= int(ModelVolumeType::MODEL_PART) && vol_type <= int(ModelVolumeType::SUPPORT_ENFORCER)); + + if (!node->has_warning_icon() && !node->has_lock()) { + node->SetBitmap(is_volume_node ? m_volume_bmps.at(vol_type) : m_empty_bmp); + return; + } + + std::string scaled_bitmap_name = std::string(); + if (node->has_warning_icon()) + scaled_bitmap_name += node->warning_icon_name(); + if (node->has_lock()) + scaled_bitmap_name += LockIcon; + if (is_volume_node) + scaled_bitmap_name += std::to_string(vol_type); + + wxBitmap *bmp = m_bitmap_cache->find(scaled_bitmap_name); + if (!bmp) { + std::vector bmps; + if (node->has_warning_icon()) + bmps.emplace_back(node->warning_icon_name() == WarningIcon ? m_warning_bmp : m_warning_manifold_bmp); + if (node->has_lock()) + bmps.emplace_back(m_lock_bmp); + if (is_volume_node) + bmps.emplace_back(m_volume_bmps[vol_type]); + bmp = m_bitmap_cache->insert(scaled_bitmap_name, bmps); + } + + node->SetBitmap(*bmp); +} + +void ObjectDataViewModel::UpdateBitmapForNode(ObjectDataViewModelNode *node, bool has_lock) +{ + node->SetLock(has_lock); + UpdateBitmapForNode(node); +} + +wxDataViewItem ObjectDataViewModel::AddObject(ModelObject *model_object, std::string warning_bitmap, bool has_lock, bool refresh) { // get object node params wxString name = from_u8(model_object->name); @@ -500,6 +555,7 @@ wxDataViewItem ObjectDataViewModel::AddObject(ModelObject* model_object, std::st const wxString extruder_str = wxString::Format("%d", extruder); auto obj_node = new ObjectDataViewModelNode(name, extruder_str, plate_idx, model_object); obj_node->SetWarningBitmap(GetWarningBitmap(warning_bitmap), warning_bitmap); + UpdateBitmapForNode(obj_node, has_lock); if (plate_node != nullptr) { obj_node->m_parent = plate_node; @@ -1151,7 +1207,7 @@ void ObjectDataViewModel::DeleteSettings(const wxDataViewItem& parent) node->set_action_icon(false); ItemChanged(parent); #else - if volume has a "settings"item, than delete it before volume deleting + // if volume has a "settings"item, than delete it before volume deleting if (node->GetChildCount() > 0 && node->GetNthChild(0)->GetType() == itSettings) { auto settings_node = node->GetNthChild(0); auto settings_item = wxDataViewItem(settings_node); @@ -1557,6 +1613,9 @@ void ObjectDataViewModel::GetValue(wxVariant &variant, const wxDataViewItem &ite case colSupportPaint: variant << node->m_support_icon; break; + case colSinking: + variant << node->m_sinking_icon; + break; case colColorPaint: variant << node->m_color_icon; break; @@ -2081,6 +2140,14 @@ bool ObjectDataViewModel::IsSupportPainted(wxDataViewItem& item) const return node->m_support_enable; } +bool ObjectDataViewModel::IsSinked(wxDataViewItem &item) const +{ + ObjectDataViewModelNode *node = static_cast(item.GetID()); + if (!node) return false; + + return node->m_sink_enable; +} + void ObjectDataViewModel::SetColorPaintState(const bool painted, wxDataViewItem obj_item) { ObjectDataViewModelNode* node = static_cast(obj_item.GetID()); @@ -2101,11 +2168,21 @@ void ObjectDataViewModel::SetSupportPaintState(const bool painted, wxDataViewIte ItemChanged(obj_item); } +void ObjectDataViewModel::SetSinkState(const bool painted, wxDataViewItem obj_item) +{ + ObjectDataViewModelNode *node = static_cast(obj_item.GetID()); + if (!node) return; + + node->set_sinking_icon(painted); + ItemChanged(obj_item); +} + void ObjectDataViewModel::Rescale() { m_volume_bmps = MenuFactory::get_volume_bitmaps(); m_warning_bmp = create_scaled_bitmap(WarningIcon); m_warning_manifold_bmp = create_scaled_bitmap(WarningManifoldIcon); + m_lock_bmp = create_scaled_bitmap(LockIcon); for (auto item : INFO_ITEMS) m_info_bmps[item.first] = create_scaled_bitmap(item.second.bmp_name); @@ -2131,8 +2208,10 @@ void ObjectDataViewModel::Rescale() break; case itLayerRoot: node->m_bmp = create_scaled_bitmap(LayerRootIcon); + break; case itLayer: node->m_bmp = create_scaled_bitmap(LayerIcon); + break; case itInfo: node->m_bmp = m_info_bmps.at(node->m_info_item_type); break; @@ -2224,6 +2303,26 @@ void ObjectDataViewModel::UpdateWarningIcon(const wxDataViewItem& item, const st AddWarningIcon(item, warning_icon_name); } +void ObjectDataViewModel::UpdateCutObjectIcon(const wxDataViewItem &item, bool has_lock) +{ + if (!item.IsOk()) + return; + ObjectDataViewModelNode* node = static_cast(item.GetID()); + if (node->has_lock() == has_lock) + return; + + node->SetLock(has_lock); + UpdateBitmapForNode(node); + + if (node->GetType() & itObject) { + wxDataViewItemArray children; + GetChildren(item, children); + for (const wxDataViewItem &child : children) + UpdateCutObjectIcon(child, has_lock); + } + ItemChanged(item); +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp index ec7eac522d..9aa40f510b 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.hpp +++ b/src/slic3r/GUI/ObjectDataViewModel.hpp @@ -42,6 +42,7 @@ enum ColumnNumber // BBS colSupportPaint , colColorPaint , + colSinking , colEditing , // item editing colCount , }; @@ -60,6 +61,7 @@ enum class InfoItemType //CustomSeam, MmuSegmentation, //Sinking + CutConnectors, }; class ObjectDataViewModelNode; @@ -87,9 +89,11 @@ class ObjectDataViewModelNode // BBS wxBitmap m_support_icon; wxBitmap m_color_icon; + wxBitmap m_sinking_icon; PrintIndicator m_printable {piUndef}; wxBitmap m_printable_icon; std::string m_warning_icon_name{ "" }; + bool m_has_lock{false}; // for cut object icon std::string m_action_icon_name = ""; ModelVolumeType m_volume_type; @@ -98,6 +102,7 @@ class ObjectDataViewModelNode // BBS bool m_support_enable = false; bool m_color_enable = false; + bool m_sink_enable = false; public: PartPlate* m_part_plate; @@ -221,6 +226,7 @@ public: void SetBitmap(const wxBitmap &icon) { m_bmp = icon; } void SetExtruder(const wxString &extruder) { m_extruder = extruder; } void SetWarningBitmap(const wxBitmap& icon, const std::string& warning_icon_name) { m_bmp = icon; m_warning_icon_name = warning_icon_name; } + void SetLock(bool has_lock) { m_has_lock = has_lock; } const wxBitmap& GetBitmap() const { return m_bmp; } const wxString& GetName() const { return m_name; } ItemType GetType() const { return m_type; } @@ -236,7 +242,8 @@ public: PrintIndicator IsPrintable() const { return m_printable; } // BBS bool HasColorPainting() const { return m_color_enable; } - bool HasSupportPainting() const { return m_support_enable; } + bool HasSupportPainting() const { return m_support_enable; } + bool HasSinking() const { return m_sink_enable; } bool IsActionEnabled() const { return m_action_enable; } void UpdateExtruderAndColorIcon(wxString extruder = ""); @@ -278,6 +285,7 @@ public: // BBS void set_color_icon(bool enable); void set_support_icon(bool enable); + void set_sinking_icon(bool enable); // Set warning icon for node void set_warning_icon(const std::string& warning_icon); @@ -292,6 +300,8 @@ public: #endif /* NDEBUG */ bool invalid() const { return m_idx < -1; } bool has_warning_icon() const { return !m_warning_icon_name.empty(); } + std::string warning_icon_name() const { return m_warning_icon_name; } + bool has_lock() const { return m_has_lock; } private: friend class ObjectDataViewModel; @@ -314,6 +324,7 @@ class ObjectDataViewModel :public wxDataViewModel wxBitmap m_empty_bmp; wxBitmap m_warning_bmp; wxBitmap m_warning_manifold_bmp; + wxBitmap m_lock_bmp; ObjectDataViewModelNode* m_plate_outside; @@ -325,7 +336,7 @@ public: void Init(); wxDataViewItem AddPlate(PartPlate* part_plate, wxString name = wxEmptyString, bool refresh = true); - wxDataViewItem AddObject(ModelObject* model_object, std::string warning_bitmap, bool refresh = true); + wxDataViewItem AddObject(ModelObject* model_object, std::string warning_bitmap, bool has_lock = false, bool refresh = true); wxDataViewItem AddVolumeChild( const wxDataViewItem &parent_item, const wxString &name, const Slic3r::ModelVolumeType volume_type, @@ -442,9 +453,11 @@ public: wxDataViewItem SetObjectPrintableState(PrintIndicator printable, wxDataViewItem obj_item); // BBS bool IsColorPainted(wxDataViewItem& item) const; - bool IsSupportPainted(wxDataViewItem& item) const; + bool IsSupportPainted(wxDataViewItem &item) const; + bool IsSinked(wxDataViewItem &item) const; void SetColorPaintState(const bool painted, wxDataViewItem obj_item); void SetSupportPaintState(const bool painted, wxDataViewItem obj_item); + void SetSinkState(const bool painted, wxDataViewItem obj_item); void SetAssociatedControl(wxDataViewCtrl* ctrl) { m_ctrl = ctrl; } // Rescale bitmaps for existing Items @@ -455,6 +468,7 @@ public: void AddWarningIcon(const wxDataViewItem& item, const std::string& warning_name); void DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object = false); void UpdateWarningIcon(const wxDataViewItem& item, const std::string& warning_name); + void UpdateCutObjectIcon(const wxDataViewItem &item, bool has_cut_icon); bool HasWarningIcon(const wxDataViewItem& item) const; t_layer_height_range GetLayerRangeByItem(const wxDataViewItem& item) const; @@ -475,6 +489,9 @@ private: wxBitmap& GetWarningBitmap(const std::string& warning_icon_name); void ReparentObject(ObjectDataViewModelNode* plate, ObjectDataViewModelNode* object); wxDataViewItem AddOutsidePlate(bool refresh = true); + + void UpdateBitmapForNode(ObjectDataViewModelNode *node); + void UpdateBitmapForNode(ObjectDataViewModelNode *node, bool has_lock); }; diff --git a/src/slic3r/GUI/OpenGLManager.cpp b/src/slic3r/GUI/OpenGLManager.cpp index 1e6b6cd7ec..5d8e90ead4 100644 --- a/src/slic3r/GUI/OpenGLManager.cpp +++ b/src/slic3r/GUI/OpenGLManager.cpp @@ -251,14 +251,19 @@ bool OpenGLManager::init_gl() if (GLEW_ARB_framebuffer_object) { s_framebuffers_type = EFramebufferType::Arb; BOOST_LOG_TRIVIAL(info) << "Found Framebuffer Type ARB."<< std::endl; - } - else if (GLEW_EXT_framebuffer_object) + } + else if (GLEW_EXT_framebuffer_object) { + BOOST_LOG_TRIVIAL(info) << "Found Framebuffer Type Ext."<< std::endl; s_framebuffers_type = EFramebufferType::Ext; - else + } + else { s_framebuffers_type = EFramebufferType::Unknown; + BOOST_LOG_TRIVIAL(warning) << "Found Framebuffer Type unknown!"<< std::endl; + } bool valid_version = s_gl_info.is_version_greater_or_equal_to(2, 0); if (!valid_version) { + BOOST_LOG_TRIVIAL(warning) << "Found opengl version <= 2.0"<< std::endl; // Complain about the OpenGL version. wxString message = from_u8((boost::format( _utf8(L("The application cannot run normally because OpenGL version is lower than 2.0.\n")))).str()); diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index b90c7b20df..145ebb85e9 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -287,7 +287,7 @@ void OptionsGroup::activate_line(Line& line) const auto h_sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(h_sizer, 1, wxEXPAND | wxALL, wxOSX ? 0 : 5); if (is_window_field(field)) - h_sizer->Add(field->getWindow(), 1, wxEXPAND | wxLEFT, titleWidth * wxGetApp().em_unit()); + h_sizer->Add(field->getWindow(), 1, wxEXPAND | wxLEFT, option.opt.multiline ? 0 : titleWidth * wxGetApp().em_unit()); if (is_sizer_field(field)) h_sizer->Add(field->getSizer(), 1, wxEXPAND | wxLEFT, titleWidth * wxGetApp().em_unit()); return; diff --git a/src/slic3r/GUI/ParamsPanel.cpp b/src/slic3r/GUI/ParamsPanel.cpp index 9fc6c1b768..bfbb6fd9f6 100644 --- a/src/slic3r/GUI/ParamsPanel.cpp +++ b/src/slic3r/GUI/ParamsPanel.cpp @@ -404,6 +404,10 @@ void ParamsPanel::create_layout() m_left_sizer->Add( m_tab_print_part, 0, wxEXPAND ); } + if (m_tab_print_layer) { + m_left_sizer->Add(m_tab_print_layer, 0, wxEXPAND); + } + if (m_tab_filament) { if (m_staticline_filament) m_left_sizer->Add(m_staticline_filament, 0, wxEXPAND); @@ -488,6 +492,7 @@ void ParamsPanel::refresh_tabs() if (m_top_panel) { m_tab_print_object = wxGetApp().get_model_tab(); m_tab_print_part = wxGetApp().get_model_tab(true); + m_tab_print_layer = wxGetApp().get_layer_tab(); } return; } @@ -557,6 +562,8 @@ void ParamsPanel::set_active_tab(wxPanel* tab) cur_tab = (Tab*) m_tab_print; } else if (m_tab_print_part && ((TabPrintModel*) m_tab_print_part)->has_model_config()) { cur_tab = (Tab*) m_tab_print_part; + } else if (m_tab_print_layer && ((TabPrintModel*)m_tab_print_layer)->has_model_config()) { + cur_tab = (Tab*)m_tab_print_layer; } else if (m_tab_print_object && ((TabPrintModel*) m_tab_print_object)->has_model_config()) { cur_tab = (Tab*) m_tab_print_object; } @@ -578,6 +585,7 @@ void ParamsPanel::set_active_tab(wxPanel* tab) {m_tab_print, m_staticline_print}, {m_tab_print_object, m_staticline_print_object}, {m_tab_print_part, m_staticline_print_part}, + {m_tab_print_layer, nullptr}, {m_tab_filament, m_staticline_filament}, {m_tab_printer, m_staticline_printer}})) { if (!t.first) continue; @@ -650,7 +658,7 @@ void ParamsPanel::msw_rescale() ((SwitchButton* )m_mode_region)->Rescale(); if (m_mode_view) ((SwitchButton* )m_mode_view)->Rescale(); - for (auto tab : {m_tab_print, m_tab_print_object, m_tab_print_part, m_tab_filament, m_tab_printer}) { + for (auto tab : {m_tab_print, m_tab_print_object, m_tab_print_part, m_tab_print_layer, m_tab_filament, m_tab_printer}) { if (tab) dynamic_cast(tab)->msw_rescale(); } //((Button*)m_export_to_file)->Rescale(); diff --git a/src/slic3r/GUI/ParamsPanel.hpp b/src/slic3r/GUI/ParamsPanel.hpp index c39f7cfbc9..7e752aa335 100644 --- a/src/slic3r/GUI/ParamsPanel.hpp +++ b/src/slic3r/GUI/ParamsPanel.hpp @@ -93,6 +93,7 @@ class ParamsPanel : public wxPanel wxPanel* m_tab_print_object { nullptr }; wxStaticLine* m_staticline_print_object { nullptr }; wxPanel* m_tab_print_part { nullptr }; + wxPanel* m_tab_print_layer { nullptr }; wxStaticLine* m_staticline_print_part { nullptr }; wxStaticLine* m_staticline_filament { nullptr }; //wxBoxSizer* m_filament_sizer { nullptr }; diff --git a/src/slic3r/GUI/PartPlate.cpp b/src/slic3r/GUI/PartPlate.cpp index d7a4bfeda5..66e5799a50 100644 --- a/src/slic3r/GUI/PartPlate.cpp +++ b/src/slic3r/GUI/PartPlate.cpp @@ -171,17 +171,21 @@ void PartPlate::init() m_plate_name_vbo_id = 0; } -BedType PartPlate::get_bed_type() const +BedType PartPlate::get_bed_type(bool load_from_project) const { std::string bed_type_key = "curr_bed_type"; - // should be called in GUI context - assert(m_plater != nullptr); if (m_config.has(bed_type_key)) { BedType bed_type = m_config.opt_enum(bed_type_key); return bed_type; } + if (!load_from_project || !m_plater || !wxGetApp().preset_bundle) + return btDefault; + + DynamicConfig& proj_cfg = wxGetApp().preset_bundle->project_config; + if (proj_cfg.has(bed_type_key)) + return proj_cfg.opt_enum(bed_type_key); return btDefault; } @@ -258,9 +262,6 @@ PrintSequence PartPlate::get_print_seq() const { std::string print_seq_key = "print_sequence"; - // should be called in GUI context - assert(m_plater != nullptr); - if (m_config.has(print_seq_key)) { PrintSequence print_seq = m_config.opt_enum(print_seq_key); return print_seq; @@ -1344,10 +1345,14 @@ std::vector PartPlate::get_extruders(bool conside_custom_gcode) const if (conside_custom_gcode) { //BBS - if (m_model->plates_custom_gcodes.find(m_plate_index) != m_model->plates_custom_gcodes.end()) { - for (auto item : m_model->plates_custom_gcodes.at(m_plate_index).gcodes) { - if (item.type == CustomGCode::Type::ToolChange) - plate_extruders.push_back(item.extruder); + int nums_extruders = 0; + if (const ConfigOptionStrings *color_option = dynamic_cast(wxGetApp().preset_bundle->project_config.option("filament_colour"))) { + nums_extruders = color_option->values.size(); + if (m_model->plates_custom_gcodes.find(m_plate_index) != m_model->plates_custom_gcodes.end()) { + for (auto item : m_model->plates_custom_gcodes.at(m_plate_index).gcodes) { + if (item.type == CustomGCode::Type::ToolChange && item.extruder <= nums_extruders) + plate_extruders.push_back(item.extruder); + } } } } @@ -1731,6 +1736,8 @@ bool PartPlate::check_outside(int obj_id, int instance_id, BoundingBoxf3* boundi m_origin.z() + m_height + Slic3r::BuildVolume::SceneEpsilon); Vec3d low_point = m_bounding_box.min + Vec3d(-Slic3r::BuildVolume::SceneEpsilon, -Slic3r::BuildVolume::SceneEpsilon, m_origin.z() - Slic3r::BuildVolume::SceneEpsilon); + Polygon hull = instance->convex_hull_2d(); + if (instance_box.max.z() > low_point.z()) low_point.z() += instance_box.min.z(); // not considering outsize if sinking BoundingBoxf3 plate_box(low_point, up_point); if (plate_box.contains(instance_box)) @@ -2449,21 +2456,26 @@ int PartPlate::load_gcode_from_file(const std::string& filename) // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("Failed to rename the output G-code file from %1% to %2%, error code %3%") % filename.c_str() % path.c_str() % //error.message(); return -1; //} - if (boost::filesystem::exists(filename)) { - assert(m_tmp_gcode_path.empty()); - m_tmp_gcode_path = filename; - m_gcode_result->filename = filename; - m_print->set_gcode_file_ready(); + if (boost::filesystem::exists(filename)) { + assert(m_tmp_gcode_path.empty()); + m_tmp_gcode_path = filename; + m_gcode_result->filename = filename; + m_print->set_gcode_file_ready(); - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": from %1% to %2%, finished") % filename.c_str() % filename.c_str(); - } + update_slice_result_valid_state(true); + + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": found valid gcode file %1%") % filename.c_str(); + } + else { + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": can not find gcode file %1%") % filename.c_str(); + ret = -1; + } - update_slice_result_valid_state(true); m_ready_for_slice = true; return ret; } -int PartPlate::load_thumbnail_data(std::string filename) +int PartPlate::load_thumbnail_data(std::string filename, ThumbnailData& thumb_data) { bool result = true; wxImage img; @@ -2472,11 +2484,11 @@ int PartPlate::load_thumbnail_data(std::string filename) img = img.Mirror(false); } if (result) { - thumbnail_data.set(img.GetWidth(), img.GetHeight()); + thumb_data.set(img.GetWidth(), img.GetHeight()); for (int i = 0; i < img.GetWidth() * img.GetHeight(); i++) { - memcpy(&thumbnail_data.pixels[4 * i], (unsigned char*)(img.GetData() + 3 * i), 3); + memcpy(&thumb_data.pixels[4 * i], (unsigned char*)(img.GetData() + 3 * i), 3); if (img.HasAlpha()) { - thumbnail_data.pixels[4 * i + 3] = *(unsigned char*)(img.GetAlpha() + i); + thumb_data.pixels[4 * i + 3] = *(unsigned char*)(img.GetAlpha() + i); } } } else { @@ -2487,7 +2499,7 @@ int PartPlate::load_thumbnail_data(std::string filename) int PartPlate::load_pattern_thumbnail_data(std::string filename) { - bool result = true; + /*bool result = true; wxImage img; result = load_image(filename, img); if (result) { @@ -2501,7 +2513,7 @@ int PartPlate::load_pattern_thumbnail_data(std::string filename) } else { return -1; - } + }*/ return 0; } @@ -3520,6 +3532,8 @@ int PartPlateList::notify_instance_update(int obj_id, int instance_id) PartPlate* plate = m_plate_list[obj_id - 1000]; plate->update_slice_result_valid_state( false ); plate->thumbnail_data.reset(); + plate->top_thumbnail_data.reset(); + plate->pick_thumbnail_data.reset(); return 0; } @@ -3548,10 +3562,14 @@ int PartPlateList::notify_instance_update(int obj_id, int instance_id) plate->update_states(); plate->update_slice_result_valid_state(); plate->thumbnail_data.reset(); + plate->top_thumbnail_data.reset(); + plate->pick_thumbnail_data.reset(); return 0; } plate->update_slice_result_valid_state(); plate->thumbnail_data.reset(); + plate->top_thumbnail_data.reset(); + plate->pick_thumbnail_data.reset(); } else if (unprintable_plate.contain_instance(obj_id, instance_id)) { @@ -3582,6 +3600,8 @@ int PartPlateList::notify_instance_update(int obj_id, int instance_id) plate->add_instance(obj_id, instance_id, false, &boundingbox); plate->update_slice_result_valid_state(); plate->thumbnail_data.reset(); + plate->top_thumbnail_data.reset(); + plate->pick_thumbnail_data.reset(); BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": add it to new plate %1%") % i; return 0; } @@ -3617,6 +3637,8 @@ int PartPlateList::notify_instance_removed(int obj_id, int instance_id) plate->remove_instance(obj_id, instance_to_delete); plate->update_slice_result_valid_state(); plate->thumbnail_data.reset(); + plate->top_thumbnail_data.reset(); + plate->pick_thumbnail_data.reset(); } if (unprintable_plate.contain_instance(obj_id, instance_to_delete)) @@ -4058,7 +4080,7 @@ void PartPlateList::postprocess_arrange_polygon(arrangement::ArrangePolygon& arr { // outarea for large object arrange_polygon.bed_idx = m_plate_list.size(); - BoundingBox apbox(arrange_polygon.poly); + BoundingBox apbox = get_extents(arrange_polygon.poly); auto apbox_size = apbox.size(); //arrange_polygon.translation(X) = scaled(0.5 * plate_stride_x()); @@ -4512,6 +4534,11 @@ int PartPlateList::store_to_3mf_structure(PlateDataPtrs& plate_data_list, bool w %(i+1) %plate_data_item->plate_thumbnail.width %plate_data_item->plate_thumbnail.height %plate_data_item->plate_thumbnail.pixels.size(); plate_data_item->config.apply(*m_plate_list[i]->config()); + if (m_plate_list[i]->top_thumbnail_data.is_valid()) + plate_data_item->top_file = "valid_top"; + if (m_plate_list[i]->pick_thumbnail_data.is_valid()) + plate_data_item->pick_file = "valid_pick"; + if (m_plate_list[i]->obj_to_instance_set.size() > 0) { for (std::set>::iterator it = m_plate_list[i]->obj_to_instance_set.begin(); it != m_plate_list[i]->obj_to_instance_set.end(); ++it) @@ -4526,8 +4553,8 @@ int PartPlateList::store_to_3mf_structure(PlateDataPtrs& plate_data_list, bool w // BBS only include current palte_idx if (plate_idx == i || plate_idx == PLATE_CURRENT_IDX || plate_idx == PLATE_ALL_IDX) { //load calibration thumbnail - if (m_plate_list[i]->cali_thumbnail_data.is_valid()) - plate_data_item->pattern_file = "valid_pattern"; + //if (m_plate_list[i]->cali_thumbnail_data.is_valid()) + // plate_data_item->pattern_file = "valid_pattern"; if (m_plate_list[i]->cali_bboxes_data.is_valid()) plate_data_item->pattern_bbox_file = "valid_pattern_bbox"; plate_data_item->gcode_file = m_plate_list[i]->m_gcode_result->filename; @@ -4610,17 +4637,29 @@ int PartPlateList::load_from_3mf_structure(PlateDataPtrs& plate_data_list) if (m_plater && !plate_data_list[i]->thumbnail_file.empty()) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": plate %1%, load thumbnail from %2%.")%(i+1) %plate_data_list[i]->thumbnail_file; if (boost::filesystem::exists(plate_data_list[i]->thumbnail_file)) { - m_plate_list[index]->load_thumbnail_data(plate_data_list[i]->thumbnail_file); + m_plate_list[index]->load_thumbnail_data(plate_data_list[i]->thumbnail_file, m_plate_list[index]->thumbnail_data); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ <thumbnail_data.width %m_plate_list[index]->thumbnail_data.height %m_plate_list[index]->thumbnail_data.pixels.size(); } } - if (m_plater && !plate_data_list[i]->pattern_file.empty()) { + /*if (m_plater && !plate_data_list[i]->pattern_file.empty()) { if (boost::filesystem::exists(plate_data_list[i]->pattern_file)) { //no need to load pattern data currently //m_plate_list[index]->load_pattern_thumbnail_data(plate_data_list[i]->pattern_file); } + }*/ + if (m_plater && !plate_data_list[i]->top_file.empty()) { + if (boost::filesystem::exists(plate_data_list[i]->top_file)) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": plate %1%, load top_thumbnail from %2%.")%(i+1) %plate_data_list[i]->top_file; + m_plate_list[index]->load_thumbnail_data(plate_data_list[i]->top_file, m_plate_list[index]->top_thumbnail_data); + } + } + if (m_plater && !plate_data_list[i]->pick_file.empty()) { + if (boost::filesystem::exists(plate_data_list[i]->pick_file)) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": plate %1%, load pick_thumbnail from %2%.")%(i+1) %plate_data_list[i]->pick_file; + m_plate_list[index]->load_thumbnail_data(plate_data_list[i]->pick_file, m_plate_list[index]->pick_thumbnail_data); + } } if (m_plater && !plate_data_list[i]->pattern_bbox_file.empty()) { if (boost::filesystem::exists(plate_data_list[i]->pattern_bbox_file)) { diff --git a/src/slic3r/GUI/PartPlate.hpp b/src/slic3r/GUI/PartPlate.hpp index 01c6f6583a..9c2617f0e4 100644 --- a/src/slic3r/GUI/PartPlate.hpp +++ b/src/slic3r/GUI/PartPlate.hpp @@ -222,7 +222,7 @@ public: //clear alll the instances in plate void clear(bool clear_sliced_result = true); - BedType get_bed_type() const; + BedType get_bed_type(bool load_from_project = false) const; void set_bed_type(BedType bed_type); void reset_bed_type(); DynamicPrintConfig* config() { return &m_config; } @@ -243,10 +243,13 @@ public: static const int plate_thumbnail_width = 512; static const int plate_thumbnail_height = 512; - ThumbnailData cali_thumbnail_data; + ThumbnailData top_thumbnail_data; + ThumbnailData pick_thumbnail_data; + + //ThumbnailData cali_thumbnail_data; PlateBBoxData cali_bboxes_data; - static const int cali_thumbnail_width = 2560; - static const int cali_thumbnail_height = 2560; + //static const int cali_thumbnail_width = 2560; + //static const int cali_thumbnail_height = 2560; //set the plate's index void set_index(int index); @@ -397,7 +400,7 @@ public: { bool result = m_slice_result_valid; if (result) - result = m_gcode_result ? (!m_gcode_result->toolpath_outside) : false; + result = m_gcode_result ? (!m_gcode_result->toolpath_outside && !m_gcode_result->conflict_result.has_value()) : false; return result; } @@ -429,7 +432,7 @@ public: //load gcode from file int load_gcode_from_file(const std::string& filename); //load thumbnail data from file - int load_thumbnail_data(std::string filename); + int load_thumbnail_data(std::string filename, ThumbnailData& thumb_data); //load pattern thumbnail data from file int load_pattern_thumbnail_data(std::string filename); //load pattern box data from file diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 62f5008c75..54817de0e1 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -325,6 +325,7 @@ struct Sidebar::priv ObjectList *m_object_list{ nullptr }; ObjectSettings *object_settings{ nullptr }; + ObjectLayers *object_layers{ nullptr }; wxButton *btn_export_gcode; wxButton *btn_reslice; @@ -464,7 +465,7 @@ static struct DynamicFilamentList : DynamicList wxString str; std::string type; wxGetApp().preset_bundle->filaments.find_preset(presets[i])->get_filament_type(type); - str << (i + 1) << " - " << type; + str << type; items.push_back({str, icons[i]}); } DynamicList::update(); @@ -757,7 +758,6 @@ Sidebar::Sidebar(Plater *parent) (project_config.option("flush_volumes_vector"))->values = std::vector(extruders.begin(), extruders.end()); (project_config.option("flush_multiplier"))->set(new ConfigOptionFloat(dlg.get_flush_multiplier())); - wxGetApp().app_config->set("flush_multiplier", std::to_string(dlg.get_flush_multiplier())); wxGetApp().preset_bundle->export_selections(*wxGetApp().app_config); wxGetApp().plater()->update_project_dirty_from_presets(); @@ -924,6 +924,10 @@ Sidebar::Sidebar(Plater *parent) #endif } + p->object_layers = new ObjectLayers(p->scrolled); + p->object_layers->Hide(); + p->sizer_params->Add(p->object_layers->get_sizer(), 0, wxEXPAND | wxTOP, 0); + auto *sizer = new wxBoxSizer(wxVERTICAL); sizer->Add(p->scrolled, 1, wxEXPAND); SetSizer(sizer); @@ -1285,6 +1289,7 @@ void Sidebar::sys_color_changed() // BBS obj_list()->sys_color_changed(); + obj_layers()->sys_color_changed(); // BBS //p->object_manipulation->sys_color_changed(); @@ -1461,6 +1466,9 @@ void Sidebar::sync_ams_list() wxGetApp().get_tab(Preset::TYPE_FILAMENT)->select_preset(wxGetApp().preset_bundle->filament_presets[0]); wxGetApp().preset_bundle->export_selections(*wxGetApp().app_config); dynamic_filament_list.update(); + // Expand filament list + p->m_panel_filament_content->SetMaxSize({-1, -1}); + Layout(); } ObjectList* Sidebar::obj_list() @@ -1475,6 +1483,11 @@ ObjectSettings* Sidebar::obj_settings() return p->object_settings; } +ObjectLayers* Sidebar::obj_layers() +{ + return p->object_layers; +} + wxPanel* Sidebar::scrolled_panel() { return p->scrolled; @@ -1939,7 +1952,7 @@ struct Plater::priv void select_all(); void deselect_all(); void remove(size_t obj_idx); - void delete_object_from_model(size_t obj_idx, bool refresh_immediately = true); //BBS + bool delete_object_from_model(size_t obj_idx, bool refresh_immediately = true); //BBS void delete_all_objects_from_model(); void reset(bool apply_presets_change = false); void center_selection(); @@ -2126,7 +2139,8 @@ struct Plater::priv #endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT //BBS: add plate_id for thumbnail - void generate_thumbnail(ThumbnailData& data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, Camera::EType camera_type); + void generate_thumbnail(ThumbnailData& data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, + Camera::EType camera_type, bool use_top_view = false, bool for_picking = false); ThumbnailsList generate_thumbnails(const ThumbnailsParams& params, Camera::EType camera_type); //BBS void generate_calibration_thumbnail(ThumbnailData& data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params); @@ -2456,12 +2470,13 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) plate->update_slice_result_valid_state(false); } } + set_plater_dirty(true); preview->on_tick_changed(tick_event_type); // update slice and print button wxGetApp().mainframe->update_slice_print_status(MainFrame::SlicePrintEventType::eEventSliceUpdate, true, false); - set_need_update(true); + update(); }); } if (wxGetApp().is_gcode_viewer()) @@ -3266,9 +3281,14 @@ std::vector Plater::priv::load_files(const std::vector& input_ if (!config_substitutions.empty()) show_substitutions_info(config_substitutions.substitutions, filename.string()); // BBS - this->model.plates_custom_gcodes = model.plates_custom_gcodes; - this->model.design_info = model.design_info; - this->model.model_info = model.model_info; + if (load_model && !load_config) { + ; + } + else { + this->model.plates_custom_gcodes = model.plates_custom_gcodes; + this->model.design_info = model.design_info; + this->model.model_info = model.model_info; + } } if (load_config) { @@ -3491,7 +3511,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ // convert_model_if(model, answer_convert_from_imperial_units == wxID_YES); } - if (model.looks_like_multipart_object()) { + if (!is_project_file && model.looks_like_multipart_object()) { MessageDialog msg_dlg(q, _L( "This file contains several objects positioned at multiple heights.\n" "Instead of considering them as multiple objects, should \n" @@ -3557,9 +3577,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ q->model().load_from(model); load_auxiliary_files(); } - // BBS: don't allow negative_z when load model objects - // auto loaded_idxs = load_model_objects(model.objects, is_project_file); - auto loaded_idxs = load_model_objects(model.objects); + auto loaded_idxs = load_model_objects(model.objects, is_project_file); obj_idxs.insert(obj_idxs.end(), loaded_idxs.begin(), loaded_idxs.end()); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("import 3mf IMPORT_LOAD_MODEL_OBJECTS \n"); @@ -3845,7 +3863,7 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs& mode void Plater::priv::load_auxiliary_files() { std::string auxiliary_path = encode_path(q->model().get_auxiliary_file_temp_path().c_str()); - wxGetApp().mainframe->m_auxiliary->Reload(auxiliary_path); + //wxGetApp().mainframe->m_project->Reload(auxiliary_path); } fs::path Plater::priv::get_export_file_path(GUI::FileType file_type) @@ -4059,13 +4077,31 @@ void Plater::priv::remove(size_t obj_idx) } -void Plater::priv::delete_object_from_model(size_t obj_idx, bool refresh_immediately) +bool Plater::priv::delete_object_from_model(size_t obj_idx, bool refresh_immediately) { + // check if object isn't cut + // show warning message that "cut consistancy" will not be supported any more + ModelObject *obj = model.objects[obj_idx]; + if (obj->is_cut()) { + InfoDialog dialog(q, _L("Delete object which is a part of cut object"), + _L("You try to delete an object which is a part of a cut object.\n" + "This action will break a cut correspondence.\n" + "After that model consistency can't be guaranteed."), + false, wxYES | wxCANCEL | wxCANCEL_DEFAULT | wxICON_WARNING); + dialog.SetButtonLabel(wxID_YES, _L("Delete")); + if (dialog.ShowModal() == wxID_CANCEL) + return false; + } + std::string snapshot_label = "Delete Object"; - if (! model.objects[obj_idx]->name.empty()) - snapshot_label += ": " + model.objects[obj_idx]->name; + if (!obj->name.empty()) + snapshot_label += ": " + obj->name; Plater::TakeSnapshot snapshot(q, snapshot_label); m_ui_jobs.cancel_all(); + + if (obj->is_cut()) + sidebar->obj_list()->invalidate_cut_info_for_object(obj_idx); + model.delete_object(obj_idx); //BBS: notify partplate the instance removed partplate_list.notify_instance_removed(obj_idx, -1); @@ -4075,6 +4111,8 @@ void Plater::priv::delete_object_from_model(size_t obj_idx, bool refresh_immedia update(); object_list_changed(); } + + return true; } void Plater::priv::delete_all_objects_from_model() @@ -6469,9 +6507,10 @@ void Plater::priv::on_3dcanvas_mouse_dragging_finished(SimpleEvent&) } //BBS: add plate id for thumbnail generate param -void Plater::priv::generate_thumbnail(ThumbnailData& data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, Camera::EType camera_type) +void Plater::priv::generate_thumbnail(ThumbnailData& data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, + Camera::EType camera_type, bool use_top_view, bool for_picking) { - view3D->get_canvas3d()->render_thumbnail(data, w, h, thumbnail_params, camera_type); + view3D->get_canvas3d()->render_thumbnail(data, w, h, thumbnail_params, camera_type, use_top_view, for_picking); } //BBS: add plate id for thumbnail generate param @@ -6863,22 +6902,29 @@ bool Plater::priv::has_assemble_view() const bool Plater::priv::can_scale_to_print_volume() const { const BuildVolume_Type type = this->bed.build_volume().type(); - return !view3D->get_canvas3d()->get_selection().is_empty() && (type == BuildVolume_Type::Rectangle || type == BuildVolume_Type::Circle); + return !sidebar->obj_list()->has_selected_cut_object() + && !view3D->get_canvas3d()->get_selection().is_empty() + && (type == BuildVolume_Type::Rectangle || type == BuildVolume_Type::Circle); } #endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT bool Plater::priv::can_mirror() const { - return get_selection().is_from_single_instance(); + return !sidebar->obj_list()->has_selected_cut_object() + && get_selection().is_from_single_instance(); } bool Plater::priv::can_replace_with_stl() const { - return get_selection().get_volume_idxs().size() == 1; + return !sidebar->obj_list()->has_selected_cut_object() + && get_selection().get_volume_idxs().size() == 1; } bool Plater::priv::can_reload_from_disk() const { + if (sidebar->obj_list()->has_selected_cut_object()) + return false; + #if ENABLE_RELOAD_FROM_DISK_REWORK // collect selected reloadable ModelVolumes std::vector> selected_volumes = reloadable_volumes(model, get_selection()); @@ -7089,7 +7135,8 @@ bool Plater::priv::can_increase_instances() const return false; int obj_idx = get_selected_object_idx(); - return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()); + return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) + && !sidebar->obj_list()->has_selected_cut_object(); } bool Plater::priv::can_decrease_instances() const @@ -7099,7 +7146,8 @@ bool Plater::priv::can_decrease_instances() const return false; int obj_idx = get_selected_object_idx(); - return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && (model.objects[obj_idx]->instances.size() > 1); + return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && (model.objects[obj_idx]->instances.size() > 1) + && !sidebar->obj_list()->has_selected_cut_object(); } bool Plater::priv::can_split_to_objects() const @@ -7566,7 +7614,7 @@ int Plater::new_project(bool skip_confirm, bool silent, const wxString& project_ (yes_or_no ? _L("You can keep the modified presets to the new project or discard them") : _L("You can keep the modifield presets to the new project, discard or save changes as new presets.")); using ab = UnsavedChangesDialog::ActionButtons; - int act_buttons = ab::KEEP; + int act_buttons = ab::KEEP | ab::REMEMBER_CHOISE; if (!yes_or_no) act_buttons |= ab::SAVE; return wxGetApp().check_and_keep_current_preset_changes(_L("Creating a new project"), header, act_buttons, &transfer_preset_changes); @@ -7701,6 +7749,8 @@ void Plater::load_project(wxString const& filename2, // if res is empty no data has been loaded if (!res.empty() && (load_restore || !(strategy & LoadStrategy::Silence))) { p->set_project_filename(load_restore ? originfile : filename); + if (load_restore && originfile.IsEmpty()) + p->set_project_name(_L("Untitled")); } else { if (using_exported_file()) p->set_project_filename(filename); @@ -7772,15 +7822,13 @@ int Plater::save_project(bool saveAs) //BBS import model by model id void Plater::import_model_id(const std::string& download_info) { - std::string download_url = wxGetApp().get_download_model_url(); - std::string filename = wxGetApp().get_download_model_name(); + std::string download_url; + std::string filename; - /* auto selection_data_arr = wxSplit(download_info, '|'); - - if (selection_data_arr.size() == 2) { - download_url = selection_data_arr[0].ToStdString(); - filename = selection_data_arr[1].ToStdString(); - }*/ + std::string download_origin_url = wxGetApp().url_decode(download_info); + fs::path download_path = fs::path(download_origin_url); + download_url = download_origin_url; + filename = download_path.filename().string(); bool download_ok = false; @@ -7957,9 +8005,10 @@ void Plater::download_project(const wxString& project_id) return; } -void Plater::request_model_download() +void Plater::request_model_download(std::string url) { wxCommandEvent* event = new wxCommandEvent(EVT_IMPORT_MODEL_ID); + event->SetString(url); wxQueueEvent(this, event); } @@ -9218,14 +9267,21 @@ int GUI::Plater::close_with_confirm(std::function second_check) return wxID_NO; } - auto result = MessageDialog(static_cast(this), _L("The current project has unsaved changes, save it before continue?"), - wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Save"), wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxCENTRE).ShowModal(); + MessageDialog dlg(static_cast(this), _L("The current project has unsaved changes, save it before continue?"), + wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Save"), wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxCENTRE); + dlg.show_dsa_button(_L("Remember my choice.")); + auto choise = wxGetApp().app_config->get("save_project_choise"); + auto result = choise.empty() ? dlg.ShowModal() : choise == "yes" ? wxID_YES : wxID_NO; if (result == wxID_CANCEL) return result; - else if (result == wxID_YES) { - result = save_project(); - if (result == wxID_CANCEL) - return result; + else { + if (dlg.get_checkbox_state()) + wxGetApp().app_config->set("save_project_choise", result == wxID_YES ? "yes" : "no"); + if (result == wxID_YES) { + result = save_project(); + if (result == wxID_CANCEL) + return result; + } } if (second_check && !second_check(result == wxID_YES)) return wxID_CANCEL; @@ -9247,7 +9303,7 @@ void Plater::trigger_restore_project(int skip_confirm) } //BBS -void Plater::delete_object_from_model(size_t obj_idx, bool refresh_immediately) { p->delete_object_from_model(obj_idx, refresh_immediately); } +bool Plater::delete_object_from_model(size_t obj_idx, bool refresh_immediately) { return p->delete_object_from_model(obj_idx, refresh_immediately); } //BBS: delete all from model void Plater::delete_all_objects_from_model() @@ -9459,8 +9515,6 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, std::array plane if (! attributes.has(ModelObjectCutAttribute::KeepUpper) && ! attributes.has(ModelObjectCutAttribute::KeepLower)) return; - Plater::TakeSnapshot snapshot(this, "Cut by Plane"); - wxBusyCursor wait; // BBS: replace z with plane_points const auto new_objects = object->cut(instance_idx, plane_points, attributes); @@ -9468,6 +9522,14 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, std::array plane remove(obj_idx); p->load_model_objects(new_objects); + // now process all updates of the 3d scene + update(); + + // Update InfoItems in ObjectList after update() to use of a correct value of the GLCanvas3D::is_sinking(), + // which is updated after a view3D->reload_scene(false, flags & (unsigned int)UpdateParams::FORCE_FULL_SCREEN_REFRESH) call + for (size_t idx = 0; idx < p->model.objects.size(); idx++) + wxGetApp().obj_list()->update_info_items(idx); + Selection& selection = p->get_selection(); size_t last_id = p->model.objects.size() - 1; for (size_t i = 0; i < new_objects.size(); ++i) @@ -9903,6 +9965,7 @@ void Plater::export_stl(bool extended, bool selection_only) // BBS: backup int Plater::export_3mf(const boost::filesystem::path& output_path, SaveStrategy strategy, int export_plate_idx, Export3mfProgressFn proFn) { + int ret = 0; //if (p->model.objects.empty()) { // MessageDialog dialog(nullptr, _L("No objects to export."), _L("Save project"), wxYES); // if (dialog.ShowModal() == wxYES) @@ -9928,6 +9991,8 @@ int Plater::export_3mf(const boost::filesystem::path& output_path, SaveStrategy //BBS: add plate logic for thumbnail generate std::vector thumbnails; std::vector calibration_thumbnails; + std::vector top_thumbnails; + std::vector picking_thumbnails; std::vector plate_bboxes; // BBS: backup if (!(strategy & SaveStrategy::Backup)) { @@ -9940,22 +10005,50 @@ int Plater::export_3mf(const boost::filesystem::path& output_path, SaveStrategy else { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": re-generate thumbnail for plate %1%") % i; const ThumbnailsParams thumbnail_params = { {}, false, true, true, true, i }; - p->generate_thumbnail(p->partplate_list.get_plate(i)->thumbnail_data, THUMBNAIL_SIZE_3MF.first, THUMBNAIL_SIZE_3MF.second, thumbnail_params, Camera::EType::Ortho); + p->generate_thumbnail(p->partplate_list.get_plate(i)->thumbnail_data, THUMBNAIL_SIZE_3MF.first, THUMBNAIL_SIZE_3MF.second, + thumbnail_params, Camera::EType::Ortho); } thumbnails.push_back(thumbnail_data); - ThumbnailData* calibration_data = &p->partplate_list.get_plate(i)->cali_thumbnail_data; - calibration_thumbnails.push_back(calibration_data); + //ThumbnailData* calibration_data = &p->partplate_list.get_plate(i)->cali_thumbnail_data; + //calibration_thumbnails.push_back(calibration_data); PlateBBoxData* plate_bbox_data = &p->partplate_list.get_plate(i)->cali_bboxes_data; plate_bboxes.push_back(plate_bbox_data); + + //generate top and picking thumbnails + ThumbnailData* top_thumbnail = &p->partplate_list.get_plate(i)->top_thumbnail_data; + if (top_thumbnail->is_valid() && using_exported_file()) { + //no need to generate thumbnail + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": non need to re-generate top_thumbnail for gcode/exported mode of plate %1%")%i; + } + else { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": re-generate top_thumbnail for plate %1%") % i; + const ThumbnailsParams thumbnail_params = { {}, false, true, false, true, i }; + p->generate_thumbnail(p->partplate_list.get_plate(i)->top_thumbnail_data, THUMBNAIL_SIZE_3MF.first, THUMBNAIL_SIZE_3MF.second, + thumbnail_params, Camera::EType::Ortho, true, false); + } + top_thumbnails.push_back(top_thumbnail); + + ThumbnailData* picking_thumbnail = &p->partplate_list.get_plate(i)->pick_thumbnail_data; + if (picking_thumbnail->is_valid() && using_exported_file()) { + //no need to generate thumbnail + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": non need to re-generate pick_thumbnail for gcode/exported mode of plate %1%")%i; + } + else { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": re-generate pick_thumbnail for plate %1%") % i; + const ThumbnailsParams thumbnail_params = { {}, false, true, false, true, i }; + p->generate_thumbnail(p->partplate_list.get_plate(i)->pick_thumbnail_data, THUMBNAIL_SIZE_3MF.first, THUMBNAIL_SIZE_3MF.second, + thumbnail_params, Camera::EType::Ortho, true, true); + } + picking_thumbnails.push_back(picking_thumbnail); } if (p->partplate_list.get_curr_plate()->is_slice_result_valid()) { //BBS generate BBS calibration thumbnails int index = p->partplate_list.get_curr_plate_index(); - ThumbnailData* calibration_data = calibration_thumbnails[index]; - const ThumbnailsParams calibration_params = { {}, false, true, true, true, p->partplate_list.get_curr_plate_index() }; - p->generate_calibration_thumbnail(*calibration_data, PartPlate::cali_thumbnail_width, PartPlate::cali_thumbnail_height, calibration_params); + //ThumbnailData* calibration_data = calibration_thumbnails[index]; + //const ThumbnailsParams calibration_params = { {}, false, true, true, true, p->partplate_list.get_curr_plate_index() }; + //p->generate_calibration_thumbnail(*calibration_data, PartPlate::cali_thumbnail_width, PartPlate::cali_thumbnail_height, calibration_params); if (using_exported_file()) { //do nothing } @@ -9980,6 +10073,8 @@ int Plater::export_3mf(const boost::filesystem::path& output_path, SaveStrategy store_params.project_presets = project_presets; store_params.config = export_config ? &cfg : nullptr; store_params.thumbnail_data = thumbnails; + store_params.top_thumbnail_data = top_thumbnails; + store_params.pick_thumbnail_data = picking_thumbnails; store_params.calibration_thumbnail_data = calibration_thumbnails; store_params.proFn = proFn; store_params.id_bboxes = plate_bboxes;//BBS @@ -10041,7 +10136,7 @@ int Plater::export_3mf(const boost::filesystem::path& output_path, SaveStrategy } } else { - return -1; + ret = -1; } if (project_presets.size() > 0) @@ -10060,8 +10155,20 @@ int Plater::export_3mf(const boost::filesystem::path& output_path, SaveStrategy //release the data here, as it will always be generated when export calibration_thumbnails[i]->reset(); } + for (unsigned int i = 0; i < top_thumbnails.size(); i++) + { + //release the data here, as it will always be generated when export + top_thumbnails[i]->reset(); + } + top_thumbnails.clear(); + for (unsigned int i = 0; i < picking_thumbnails.size(); i++) + { + //release the data here, as it will always be generated when export + picking_thumbnails[i]->reset();; + } + picking_thumbnails.clear(); - return 0; + return ret; } void Plater::publish_project() @@ -10330,9 +10437,7 @@ int Plater::send_gcode(int plate_idx, Export3mfProgressFn proFn) if (plate_idx == PLATE_CURRENT_IDX) { p->m_print_job_data.plate_idx = get_partplate_list().get_curr_plate_index(); } - else if (plate_idx == PLATE_ALL_IDX) { - p->m_print_job_data.plate_idx = get_partplate_list().get_curr_plate_index(); - } else { + else { p->m_print_job_data.plate_idx = plate_idx; } @@ -10564,6 +10669,7 @@ void Plater::on_config_change(const DynamicPrintConfig &config) if (update_filament_colors_in_full_config()) { p->sidebar->obj_list()->update_filament_colors(); + dynamic_filament_list.update(); continue; } } @@ -11084,6 +11190,7 @@ void Plater::sys_color_changed() p->preview->sys_color_changed(); p->sidebar->sys_color_changed(); p->menus.sys_color_changed(); + if (p->m_select_machine_dlg) p->m_select_machine_dlg->sys_color_changed(); Layout(); GetParent()->Layout(); @@ -11484,8 +11591,10 @@ int Plater::select_plate_by_hover_id(int hover_id, bool right_click) ret = select_plate(plate_index); if (!ret) { - set_prepare_state(Job::PREPARE_STATE_MENU); - arrange(); + if (last_arrange_job_is_finished()) { + set_prepare_state(Job::PREPARE_STATE_MENU); + arrange(); + } } else { @@ -11538,6 +11647,7 @@ int Plater::select_plate_by_hover_id(int hover_id, bool right_click) BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("select print sequence %1% for plate %2% at plate side")%ps_sel %plate_index; auto plate_config = *(curr_plate->config()); wxGetApp().plater()->config_change_notification(plate_config, std::string("print_sequence")); + update(); }); dlg.ShowModal(); curr_plate->set_plate_name(dlg.get_plate_name().ToStdString()); diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 44c5fef571..0ccdf24ad7 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -66,6 +66,7 @@ namespace GUI { class MainFrame; class ConfigOptionsGroup; class ObjectSettings; +class ObjectLayers; class ObjectList; class GLCanvas3D; class Mouse3DController; @@ -131,6 +132,7 @@ public: ObjectList* obj_list(); ObjectSettings* obj_settings(); + ObjectLayers* obj_layers(); wxPanel* scrolled_panel(); wxPanel* print_panel(); wxPanel* filament_panel(); @@ -214,7 +216,7 @@ public: //BBS download project by project id void import_model_id(const std::string& download_info); void download_project(const wxString& project_id); - void request_model_download(); + void request_model_download(std::string url); void request_download_project(std::string project_id); // BBS: check snapshot bool up_to_date(bool saved, bool backup); @@ -305,7 +307,7 @@ public: int close_with_confirm(std::function second_check = nullptr); // BBS close project //BBS: trigger a restore project event void trigger_restore_project(int skip_confirm = 0); - void delete_object_from_model(size_t obj_idx, bool refresh_immediately = true); // BBS support refresh immediately + bool delete_object_from_model(size_t obj_idx, bool refresh_immediately = true); // BBS support refresh immediately void delete_all_objects_from_model(); //BBS delete all objects from model void set_selected_visible(bool visible); void remove_selected(); @@ -690,6 +692,13 @@ public: std::string get_preview_only_filename() { return m_preview_only_filename; }; + bool last_arrange_job_is_finished() + { + bool prevRunning = false; + return m_arrange_running.compare_exchange_strong(prevRunning, true); + }; + std::atomic m_arrange_running{false}; + private: struct priv; std::unique_ptr p; diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 0a22ed44e9..cfaf6a6cc0 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -136,6 +136,9 @@ wxBoxSizer *PreferencesDialog::create_item_language_combobox( else if (vlist[i] == wxLocale::GetLanguageInfo(wxLANGUAGE_JAPANESE)) { language_name = wxString::FromUTF8("\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E"); } + else if (vlist[i] == wxLocale::GetLanguageInfo(wxLANGUAGE_ITALIAN)) { + language_name = wxString::FromUTF8("\x69\x74\x61\x6c\x69\x61\x6e\x6f"); + } if (app_config->get(param) == vlist[i]->CanonicalName) { m_current_language_selected = i; @@ -381,10 +384,55 @@ wxBoxSizer *PreferencesDialog::create_item_multiple_combobox( return m_sizer_tcombox; } +wxBoxSizer *PreferencesDialog::create_item_input(wxString title, wxString title2, wxWindow *parent, wxString tooltip, std::string param, std::function onchange) +{ + wxBoxSizer *sizer_input = new wxBoxSizer(wxHORIZONTAL); + auto input_title = new wxStaticText(parent, wxID_ANY, title); + input_title->SetForegroundColour(DESIGN_GRAY900_COLOR); + input_title->SetFont(::Label::Body_13); + input_title->SetToolTip(tooltip); + input_title->Wrap(-1); + + auto input = new ::TextInput(parent, wxEmptyString, wxEmptyString, wxEmptyString, wxDefaultPosition, DESIGN_INPUT_SIZE, wxTE_PROCESS_ENTER); + StateColor input_bg(std::pair(wxColour("#F0F0F1"), StateColor::Disabled), std::pair(*wxWHITE, StateColor::Enabled)); + input->SetBackgroundColor(input_bg); + input->GetTextCtrl()->SetValue(app_config->get(param)); + + auto second_title = new wxStaticText(parent, wxID_ANY, title2, wxDefaultPosition, DESIGN_TITLE_SIZE, 0); + second_title->SetForegroundColour(DESIGN_GRAY900_COLOR); + second_title->SetFont(::Label::Body_13); + second_title->SetToolTip(tooltip); + second_title->Wrap(-1); + + sizer_input->Add(0, 0, 0, wxEXPAND | wxLEFT, 23); + sizer_input->Add(input_title, 0, wxALIGN_CENTER_VERTICAL | wxALL, 3); + sizer_input->Add(input, 0, wxALIGN_CENTER_VERTICAL, 0); + sizer_input->Add(0, 0, 0, wxEXPAND | wxLEFT, 3); + sizer_input->Add(second_title, 0, wxALIGN_CENTER_VERTICAL | wxALL, 3); + + input->GetTextCtrl()->Bind(wxEVT_TEXT_ENTER, [this, param, input, onchange](wxCommandEvent &e) { + auto value = input->GetTextCtrl()->GetValue(); + app_config->set(param, std::string(value.mb_str())); + app_config->save(); + onchange(value); + e.Skip(); + }); + + input->GetTextCtrl()->Bind(wxEVT_KILL_FOCUS, [this, param, input, onchange](wxFocusEvent &e) { + auto value = input->GetTextCtrl()->GetValue(); + app_config->set(param, std::string(value.mb_str())); + app_config->save(); + onchange(value); + e.Skip(); + }); + + return sizer_input; +} + wxBoxSizer *PreferencesDialog::create_item_backup_input(wxString title, wxWindow *parent, wxString tooltip, std::string param) { wxBoxSizer *m_sizer_input = new wxBoxSizer(wxHORIZONTAL); - auto input_title = new wxStaticText(parent, wxID_ANY, title, wxDefaultPosition, DESIGN_TITLE_SIZE, 0); + auto input_title = new wxStaticText(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, 0); input_title->SetForegroundColour(DESIGN_GRAY900_COLOR); input_title->SetFont(::Label::Body_13); input_title->SetToolTip(tooltip); @@ -403,10 +451,10 @@ wxBoxSizer *PreferencesDialog::create_item_backup_input(wxString title, wxWindow second_title->Wrap(-1); m_sizer_input->Add(0, 0, 0, wxEXPAND | wxLEFT, 23); - m_sizer_input->Add(input_title, 0, wxALIGN_CENTER | wxALL, 3); - m_sizer_input->Add(input, 0, wxALIGN_CENTER, 0); + m_sizer_input->Add(input_title, 0, wxALIGN_CENTER_VERTICAL | wxALL, 3); + m_sizer_input->Add(input, 0, wxALIGN_CENTER_VERTICAL, 0); m_sizer_input->Add(0, 0, 0, wxEXPAND | wxLEFT, 3); - m_sizer_input->Add(second_title, 0, wxALIGN_CENTER| wxALL, 3); + m_sizer_input->Add(second_title, 0, wxALIGN_CENTER_VERTICAL | wxALL, 3); input->GetTextCtrl()->Bind(wxEVT_COMMAND_TEXT_UPDATED, [this, param, input](wxCommandEvent &e) { @@ -418,6 +466,9 @@ wxBoxSizer *PreferencesDialog::create_item_backup_input(wxString title, wxWindow m_backup_interval_time = input->GetTextCtrl()->GetValue(); app_config->set("backup_interval", std::string(m_backup_interval_time.mb_str())); app_config->save(); + long backup_interval = 0; + m_backup_interval_time.ToLong(&backup_interval); + Slic3r::set_backup_interval(backup_interval); e.Skip(); }); @@ -425,6 +476,9 @@ wxBoxSizer *PreferencesDialog::create_item_backup_input(wxString title, wxWindow m_backup_interval_time = input->GetTextCtrl()->GetValue(); app_config->set("backup_interval", std::string(m_backup_interval_time.mb_str())); app_config->save(); + long backup_interval = 0; + m_backup_interval_time.ToLong(&backup_interval); + Slic3r::set_backup_interval(backup_interval); e.Skip(); }); @@ -543,7 +597,7 @@ wxBoxSizer *PreferencesDialog::create_item_checkbox(wxString title, wxWindow *pa checkbox_title->SetFont(::Label::Body_13); auto size = checkbox_title->GetTextExtent(title); - checkbox_title->SetMinSize(wxSize(size.x + FromDIP(40), -1)); + checkbox_title->SetMinSize(wxSize(size.x + FromDIP(4), -1)); checkbox_title->Wrap(-1); m_sizer_checkbox->Add(checkbox_title, 0, wxALIGN_CENTER | wxALL, 3); @@ -556,6 +610,9 @@ wxBoxSizer *PreferencesDialog::create_item_checkbox(wxString title, wxWindow *pa // backup if (param == "backup_switch") { bool pbool = app_config->get("backup_switch") == "true" ? true : false; + std::string backup_interval = "10"; + app_config->get("backup_interval", backup_interval); + Slic3r::set_backup_interval(pbool ? boost::lexical_cast(backup_interval) : 0); if (m_backup_interval_textinput != nullptr) { m_backup_interval_textinput->Enable(pbool); } } @@ -615,6 +672,41 @@ wxBoxSizer *PreferencesDialog::create_item_checkbox(wxString title, wxWindow *pa return m_sizer_checkbox; } +wxBoxSizer *PreferencesDialog::create_item_button(wxString title, wxString title2, wxWindow *parent, wxString tooltip, std::function onclick) +{ + wxBoxSizer *m_sizer_checkbox = new wxBoxSizer(wxHORIZONTAL); + + m_sizer_checkbox->Add(0, 0, 0, wxEXPAND | wxLEFT, 23); + auto m_staticTextPath = new wxStaticText(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END); + // m_staticTextPath->SetMaxSize(wxSize(FromDIP(440), -1)); + m_staticTextPath->SetForegroundColour(DESIGN_GRAY900_COLOR); + m_staticTextPath->SetFont(::Label::Body_13); + m_staticTextPath->Wrap(-1); + + auto m_button_download = new Button(parent, title2); + + StateColor abort_bg(std::pair(wxColour(255, 255, 255), StateColor::Disabled), std::pair(wxColour(206, 206, 206), StateColor::Pressed), + std::pair(wxColour(238, 238, 238), StateColor::Hovered), std::pair(wxColour(255, 255, 255), StateColor::Enabled), + std::pair(wxColour(255, 255, 255), StateColor::Normal)); + m_button_download->SetBackgroundColor(abort_bg); + StateColor abort_bd(std::pair(wxColour(144, 144, 144), StateColor::Disabled), std::pair(wxColour(38, 46, 48), StateColor::Enabled)); + m_button_download->SetBorderColor(abort_bd); + StateColor abort_text(std::pair(wxColour(144, 144, 144), StateColor::Disabled), std::pair(wxColour(38, 46, 48), StateColor::Enabled)); + m_button_download->SetTextColor(abort_text); + m_button_download->SetFont(Label::Body_10); + m_button_download->SetMinSize(wxSize(FromDIP(58), FromDIP(22))); + m_button_download->SetSize(wxSize(FromDIP(58), FromDIP(22))); + m_button_download->SetCornerRadius(FromDIP(12)); + m_button_download->SetToolTip(tooltip); + + m_button_download->Bind(wxEVT_BUTTON, [this, onclick](auto &e) { onclick(); }); + + m_sizer_checkbox->Add(m_staticTextPath, 0, wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5)); + m_sizer_checkbox->Add(m_button_download, 0, wxALL, FromDIP(5)); + + return m_sizer_checkbox; +} + wxWindow* PreferencesDialog::create_item_downloads(wxWindow* parent, int padding_left, std::string param) { wxString download_path = wxString::FromUTF8(app_config->get("download_path")); @@ -801,7 +893,8 @@ wxWindow* PreferencesDialog::create_general_page() wxLANGUAGE_SWEDISH, wxLANGUAGE_DUTCH, wxLANGUAGE_HUNGARIAN, - wxLANGUAGE_JAPANESE + wxLANGUAGE_JAPANESE, + wxLANGUAGE_ITALIAN }; auto translations = wxTranslations::Get()->GetAvailableTranslations(SLIC3R_APP_KEY); @@ -826,14 +919,20 @@ wxWindow* PreferencesDialog::create_general_page() std::vector Regions = {_L("Asia-Pacific"), _L("China"), _L("Europe"), _L("North America"), _L("Others")}; auto item_region= create_item_region_combobox(_L("Login Region"), page, _L("Login Region"), Regions); - std::vector Units = {_L("Metric"), _L("Imperial")}; + std::vector Units = {_L("Metric") + " (mm, g)", _L("Imperial") + " (in, oz)"}; auto item_currency = create_item_combobox(_L("Units"), page, _L("Units"), "use_inches", Units); + auto item_mouse_zoom_settings = create_item_checkbox(_L("Zoom to mouse position"), page, _L("Zoom in towards the mouse pointer's position in the 3D view, rather than the 2D window center."), 50, "zoom_to_mouse"); + auto item_hints = create_item_checkbox(_L("Show \"Tip of the day\" notification after start"), page, _L("If enabled, useful hints are displayed at startup."), 50, "show_hints"); auto item_gcode_window = create_item_checkbox(_L("Show g-code window"), page, _L("If enabled, g-code window will be displayed."), 50, "show_gcode_window"); - auto title_sync_settings = create_item_title(_L("User sync"), page, _L("User sync")); + auto title_presets = create_item_title(_L("Presets"), page, _L("Presets")); auto item_user_sync = create_item_checkbox(_L("Auto sync user presets(Printer/Filament/Process)"), page, _L("User Sync"), 50, "sync_user_preset"); + auto item_system_sync = create_item_checkbox(_L("Update built-in Presets automatically."), page, _L("System Sync"), 50, "sync_system_preset"); + auto item_save_presets = create_item_button(_L("Clear my choice on the unsaved presets."), _L("Clear"), page, _L("Clear my choice on the unsaved presets."), []() { + wxGetApp().app_config->set("save_preset_choise", ""); + }); #ifdef _WIN32 auto title_associate_file = create_item_title(_L("Associate files to OrcaSlicer"), page, _L("Associate files to OrcaSlicer")); @@ -847,11 +946,18 @@ wxWindow* PreferencesDialog::create_general_page() _L("If enabled, sets OrcaSlicer as default application to open .step files"), 50, "associate_step"); #endif // _WIN32 - - auto title_backup = create_item_title(_L("Backup"), page, _L("Backup")); - //auto item_backup = create_item_switch(_L("Backup switch"), page, _L("Backup switch"), "units"); - auto item_backup = create_item_checkbox(_L("Auto-Backup"), page,_L("Auto-Backup"), 50, "backup_switch"); - auto item_backup_interval = create_item_backup_input(_L("Backup interval"), page, _L("Backup interval"), "backup_interval"); + auto title_project = create_item_title(_L("Project"), page, ""); + auto item_max_recent_count = create_item_input(_L("Maximum recent projects"), "", page, _L("Maximum count of recent projects"), "max_recent_count", [](wxString value) { + long max = 0; + if (value.ToLong(&max)) + wxGetApp().mainframe->set_max_recent_count(max); + }); + auto item_save_choise = create_item_button(_L("Clear my choice on the unsaved projects."), _L("Clear"), page, _L("Clear my choice on the unsaved projects."), []() { + wxGetApp().app_config->set("save_project_choise", ""); + }); + // auto item_backup = create_item_switch(_L("Backup switch"), page, _L("Backup switch"), "units"); + auto item_backup = create_item_checkbox(_L("Auto-Backup"), page,_L("Backup your project periodically for restoring from the occasional crash."), 50, "backup_switch"); + auto item_backup_interval = create_item_backup_input(_L("every"), page, _L("The peroid of backup in seconds."), "backup_interval"); //downloads auto title_downloads = create_item_title(_L("Downloads"), page, _L("Downloads")); @@ -868,19 +974,24 @@ wxWindow* PreferencesDialog::create_general_page() sizer_page->Add(item_language, 0, wxTOP, FromDIP(3)); sizer_page->Add(item_region, 0, wxTOP, FromDIP(3)); sizer_page->Add(item_currency, 0, wxTOP, FromDIP(3)); + sizer_page->Add(item_mouse_zoom_settings, 0, wxTOP, FromDIP(3)); sizer_page->Add(item_hints, 0, wxTOP, FromDIP(3)); sizer_page->Add(item_gcode_window, 0, wxTOP, FromDIP(3)); - sizer_page->Add(title_sync_settings, 0, wxTOP | wxEXPAND, FromDIP(20)); + sizer_page->Add(title_presets, 0, wxTOP | wxEXPAND, FromDIP(20)); sizer_page->Add(item_user_sync, 0, wxTOP, FromDIP(3)); + sizer_page->Add(item_system_sync, 0, wxTOP, FromDIP(3)); + sizer_page->Add(item_save_presets, 0, wxTOP, FromDIP(3)); #ifdef _WIN32 sizer_page->Add(title_associate_file, 0, wxTOP| wxEXPAND, FromDIP(20)); sizer_page->Add(item_associate_3mf, 0, wxTOP, FromDIP(3)); sizer_page->Add(item_associate_stl, 0, wxTOP, FromDIP(3)); sizer_page->Add(item_associate_step, 0, wxTOP, FromDIP(3)); #endif // _WIN32 - sizer_page->Add(title_backup, 0, wxTOP| wxEXPAND, FromDIP(20)); + sizer_page->Add(title_project, 0, wxTOP| wxEXPAND, FromDIP(20)); + sizer_page->Add(item_max_recent_count, 0, wxTOP, FromDIP(3)); + sizer_page->Add(item_save_choise, 0, wxTOP, FromDIP(3)); sizer_page->Add(item_backup, 0, wxTOP,FromDIP(3)); - sizer_page->Add(item_backup_interval, 0, wxTOP,FromDIP(3)); + item_backup->Add(item_backup_interval, 0, wxLEFT, 0); sizer_page->Add(title_downloads, 0, wxTOP| wxEXPAND, FromDIP(20)); sizer_page->Add(item_downloads, 0, wxEXPAND, FromDIP(3)); diff --git a/src/slic3r/GUI/Preferences.hpp b/src/slic3r/GUI/Preferences.hpp index cd497110d3..419242f7ce 100644 --- a/src/slic3r/GUI/Preferences.hpp +++ b/src/slic3r/GUI/Preferences.hpp @@ -110,8 +110,9 @@ public: wxBoxSizer *create_item_checkbox(wxString title, wxWindow *parent, wxString tooltip, int padding_left, std::string param); wxBoxSizer *create_item_darkmode_checkbox(wxString title, wxWindow *parent, wxString tooltip, int padding_left, std::string param); void set_dark_mode(); + wxBoxSizer *create_item_button(wxString title, wxString title2, wxWindow *parent, wxString tooltip, std::function onclick); wxWindow* create_item_downloads(wxWindow* parent, int padding_left, std::string param); - wxBoxSizer* create_item_backup_checkbox(wxString title, wxWindow* parent, wxString tooltip, int padding_left, std::string param); + wxBoxSizer *create_item_input(wxString title, wxString title2, wxWindow *parent, wxString tooltip, std::string param, std::function onchange = {}); wxBoxSizer *create_item_backup_input(wxString title, wxWindow *parent, wxString tooltip, std::string param); wxBoxSizer *create_item_multiple_combobox( wxString title, wxWindow *parent, wxString tooltip, int padding_left, std::string parama, std::vector vlista, std::vector vlistb); diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 3429739c83..8135692316 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -691,7 +691,7 @@ PlaterPresetComboBox::PlaterPresetComboBox(wxWindow *parent, Preset::Type preset m_clrData.SetChooseAlpha(false); wxColourDialog dialog(this, &m_clrData); - dialog.SetTitle("Please choose the filament colour"); + dialog.SetTitle(_L("Please choose the filament colour")); if ( dialog.ShowModal() == wxID_OK ) { m_clrData = dialog.GetColourData(); diff --git a/src/slic3r/GUI/Printer/PrinterFileSystem.cpp b/src/slic3r/GUI/Printer/PrinterFileSystem.cpp index ff018bb413..8915e5f788 100644 --- a/src/slic3r/GUI/Printer/PrinterFileSystem.cpp +++ b/src/slic3r/GUI/Printer/PrinterFileSystem.cpp @@ -78,6 +78,7 @@ void PrinterFileSystem::SetGroupMode(GroupMode mode) return; this->m_group_mode = mode; m_lock_start = m_lock_end = 0; + UpdateGroupSelect(); SendChangedEvent(EVT_MODE_CHANGED); } @@ -131,6 +132,7 @@ void PrinterFileSystem::ListAllFiles() } } BuildGroups(); + UpdateGroupSelect(); m_status = Status::ListReady; SendChangedEvent(EVT_STATUS_CHANGED, m_status); SendChangedEvent(EVT_FILE_CHANGED); @@ -253,7 +255,28 @@ size_t PrinterFileSystem::GetIndexAtTime(boost::uint32_t time) void PrinterFileSystem::ToggleSelect(size_t index) { - if (index < m_file_list.size()) { + if (m_group_mode != G_NONE) { + size_t beg = m_group_mode == G_YEAR ? m_group_month[m_group_year[index]] : m_group_month[index]; + size_t end_month = m_group_mode == G_YEAR ? ((index + 1) < m_group_year.size() ? m_group_year[index + 1] : m_group_month.size()) : index + 1; + size_t end = end_month < m_group_month.size() ? m_group_month[end_month] : m_file_list.size(); + if ((m_group_flags[index] & FF_SELECT) == 0) { + for (int i = beg; i < end; ++i) { + if ((m_file_list[i].flags & FF_SELECT) == 0) { + m_file_list[i].flags |= FF_SELECT; + ++m_select_count; + } + } + m_group_flags[index] |= FF_SELECT; + } else { + for (int i = beg; i < end; ++i) { + if (m_file_list[i].flags & FF_SELECT) { + m_file_list[i].flags &= ~FF_SELECT; + --m_select_count; + } + } + m_group_flags[index] &= ~FF_SELECT; + } + } else if (index < m_file_list.size()) { m_file_list[index].flags ^= FF_SELECT; if (m_file_list[index].flags & FF_SELECT) ++m_select_count; @@ -268,9 +291,11 @@ void PrinterFileSystem::SelectAll(bool select) if (select) { for (auto &f : m_file_list) f.flags |= FF_SELECT; m_select_count = m_file_list.size(); + for (auto &s : m_group_flags) s |= FF_SELECT; } else { for (auto &f : m_file_list) f.flags &= ~FF_SELECT; m_select_count = 0; + for (auto &s : m_group_flags) s &= ~FF_SELECT; } SendChangedEvent(EVT_SELECT_CHANGED, m_select_count); } @@ -289,6 +314,17 @@ PrinterFileSystem::File const &PrinterFileSystem::GetFile(size_t index) { if (m_group_mode == G_NONE) return m_file_list[index]; + if (m_group_mode == G_YEAR) index = m_group_year[index]; + return m_file_list[m_group_month[index]]; +} + +PrinterFileSystem::File const &PrinterFileSystem::GetFile(size_t index, bool &select) +{ + if (m_group_mode == G_NONE) { + select = m_file_list[index].IsSelect(); + return m_file_list[index]; + } + select = m_group_flags[index] & FF_SELECT; if (m_group_mode == G_YEAR) index = m_group_year[index]; return m_file_list[m_group_month[index]]; @@ -380,6 +416,26 @@ void PrinterFileSystem::BuildGroups() } } +void PrinterFileSystem::UpdateGroupSelect() +{ + m_group_flags.clear(); + int beg = 0; + if (m_group_mode != G_NONE) { + auto group = m_group_mode == G_YEAR ? m_group_year : m_group_month; + if (m_group_mode == G_YEAR) + for (auto &g : group) g = m_group_month[g]; + m_group_flags.resize(group.size(), FF_SELECT); + for (int i = 0; i < m_file_list.size(); ++i) { + if ((m_file_list[i].flags & FF_SELECT) == 0) { + auto iter = std::upper_bound(group.begin(), group.end(), i); + m_group_flags[iter - group.begin() - 1] &= ~FF_SELECT; + if (iter == group.end()) break; + i = *iter - 1; // start from next group + } + } + } +} + void PrinterFileSystem::DeleteFilesContinue() { std::vector indexes; @@ -481,14 +537,12 @@ void PrinterFileSystem::DownloadNextFile() download->index = FindFile(download->index, download->name); if (download->index != size_t(-1)) { int progress = data.size * 100 / data.total; - if (result > CONTINUE) - progress = -2; auto & file = m_file_list[download->index]; if (result == ERROR_CANCEL) file.flags &= ~FF_DOWNLOAD; else if (file.progress != progress) { file.progress = progress; - SendChangedEvent(EVT_DOWNLOAD, download->index, file.path, data.size); + SendChangedEvent(EVT_DOWNLOAD, download->index, file.path, result); } } if (result != CONTINUE) DownloadNextFile(); @@ -586,9 +640,14 @@ void PrinterFileSystem::FileRemoved(size_t index, std::string const &name) size_t index2 = removeFromGroup(m_group_month, index, m_file_list.size()); if (index2 < m_group_month.size()) { int index3 = removeFromGroup(m_group_year, index, m_group_month.size()); - if (index3 < m_group_year.size()) + if (index3 < m_group_year.size()) { m_group_year.erase(m_group_year.begin() + index3); + if (m_group_mode == G_YEAR) + m_group_flags.erase(m_group_flags.begin() + index2); + } m_group_month.erase(m_group_month.begin() + index2); + if (m_group_mode == G_MONTH) + m_group_flags.erase(m_group_flags.begin() + index2); } m_file_list.erase(m_file_list.begin() + index); } diff --git a/src/slic3r/GUI/Printer/PrinterFileSystem.h b/src/slic3r/GUI/Printer/PrinterFileSystem.h index 7757216217..66e41f2d67 100644 --- a/src/slic3r/GUI/Printer/PrinterFileSystem.h +++ b/src/slic3r/GUI/Printer/PrinterFileSystem.h @@ -146,6 +146,8 @@ public: File const &GetFile(size_t index); + File const &GetFile(size_t index, bool &select); + enum Status { Initializing, Connecting, @@ -170,6 +172,8 @@ public: private: void BuildGroups(); + void UpdateGroupSelect(); + void DeleteFilesContinue(); void DownloadNextFile(); @@ -266,6 +270,7 @@ protected: FileList m_file_list2; std::vector m_group_year; std::vector m_group_month; + std::vector m_group_flags; private: size_t m_select_count = 0; diff --git a/src/slic3r/GUI/Printer/gstbambusrc.c b/src/slic3r/GUI/Printer/gstbambusrc.c index 5b2bbd15da..0759119a2a 100644 --- a/src/slic3r/GUI/Printer/gstbambusrc.c +++ b/src/slic3r/GUI/Printer/gstbambusrc.c @@ -259,6 +259,8 @@ gst_bambusrc_get_property (GObject * object, guint prop_id, } } +int gst_bambu_last_error = 0; + static GstFlowReturn gst_bambusrc_create (GstPushSrc * psrc, GstBuffer ** outbuf) { @@ -286,7 +288,8 @@ gst_bambusrc_create (GstPushSrc * psrc, GstBuffer ** outbuf) } if (rv != Bambu_success) { - return GST_FLOW_ERROR; + gst_bambu_last_error = rv; + return GST_FLOW_ERROR; } #if GLIB_CHECK_VERSION(2,68,0) @@ -316,7 +319,7 @@ gst_bambusrc_create (GstPushSrc * psrc, GstBuffer ** outbuf) //if (GST_CLOCK_TIME_NONE == src->sttime) // src->sttime GST_DEBUG_OBJECT(src, - "sttime init to %llu.", + "sttime init to %lu.", src->sttime); } //GST_BUFFER_DTS(*outbuf) = gst_element_get_current_clock_time((GstElement *)psrc) - src->sttime; @@ -325,7 +328,7 @@ gst_bambusrc_create (GstPushSrc * psrc, GstBuffer ** outbuf) GST_BUFFER_DURATION(*outbuf) = GST_CLOCK_TIME_NONE; } GST_DEBUG_OBJECT(src, - "sttime:%llu, DTS:%llu, PTS: %llu~", + "sttime:%lu, DTS:%lu, PTS: %lu~", src->sttime, GST_BUFFER_DTS(*outbuf), GST_BUFFER_PTS(*outbuf)); return GST_FLOW_OK; @@ -377,6 +380,7 @@ gst_bambusrc_start (GstBaseSrc * bsrc) BAMBULIB(Bambu_Close)(src->tnl); BAMBULIB(Bambu_Destroy)(src->tnl); src->tnl = NULL; + gst_bambu_last_error = rv; return FALSE; } diff --git a/src/slic3r/GUI/PrivacyUpdateDialog.cpp b/src/slic3r/GUI/PrivacyUpdateDialog.cpp index 6f7e077fc1..ed2fa37f33 100644 --- a/src/slic3r/GUI/PrivacyUpdateDialog.cpp +++ b/src/slic3r/GUI/PrivacyUpdateDialog.cpp @@ -57,12 +57,8 @@ PrivacyUpdateDialog::PrivacyUpdateDialog(wxWindow* parent, wxWindowID id, const m_vebview_release_note->SetSize(wxSize(FromDIP(540), FromDIP(340))); m_vebview_release_note->SetMinSize(wxSize(FromDIP(540), FromDIP(340))); - fs::path ph(data_dir()); - ph /= "resources/tooltip/privacyupdate.html"; - if (!fs::exists(ph)) { - ph = resources_dir(); - ph /= "tooltip/privacyupdate.html"; - } + fs::path ph(resources_dir()); + ph /= "tooltip/privacyupdate.html"; m_host_url = ph.string(); std::replace(m_host_url.begin(), m_host_url.end(), '\\', '/'); m_host_url = "file:///" + m_host_url; @@ -87,7 +83,7 @@ PrivacyUpdateDialog::PrivacyUpdateDialog(wxWindow* parent, wxWindowID id, const e.Skip(); }); - m_vebview_release_note->Bind(wxEVT_WEBVIEW_NAVIGATING , &PrivacyUpdateDialog::OnNavigating, this); + //m_vebview_release_note->Bind(wxEVT_WEBVIEW_NAVIGATING , &PrivacyUpdateDialog::OnNavigating, this); m_button_ok = new Button(this, _L("Accept")); m_button_ok->SetBackgroundColor(btn_bg_green); @@ -171,6 +167,10 @@ bool PrivacyUpdateDialog::ShowReleaseNote(std::string content) void PrivacyUpdateDialog::RunScript(std::string script) { WebView::RunScript(m_vebview_release_note, script); + std::string switch_dark_mode_script = "SwitchDarkMode("; + switch_dark_mode_script += wxGetApp().app_config->get("dark_color_mode") == "1" ? "true" : "false"; + switch_dark_mode_script += ");"; + WebView::RunScript(m_vebview_release_note, switch_dark_mode_script); script.clear(); } diff --git a/src/slic3r/GUI/Project.cpp b/src/slic3r/GUI/Project.cpp new file mode 100644 index 0000000000..0a6635f9a2 --- /dev/null +++ b/src/slic3r/GUI/Project.cpp @@ -0,0 +1,379 @@ +#include "Tab.hpp" +#include "Project.hpp" +#include "libslic3r/Utils.hpp" +#include "libslic3r/Model.hpp" +#include "libslic3r/Format/bbs_3mf.hpp" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wxExtensions.hpp" +#include "GUI_App.hpp" +#include "GUI_ObjectList.hpp" +#include "MainFrame.hpp" +#include + +namespace Slic3r { namespace GUI { + +wxDEFINE_EVENT(EVT_PROJECT_RELOAD, wxCommandEvent); + +const std::vector license_list = { + "BSD License", + "Apache License", + "GPL License", + "LGPL License", + "MIT License", + "CC License" +}; + +ProjectPanel::ProjectPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style) : wxPanel(parent, id, pos, size, style) +{ + m_project_home_url = wxString::Format("file://%s/web/model/index.html", from_u8(resources_dir())); + std::string strlang = wxGetApp().app_config->get("language"); + if (strlang != "") + m_project_home_url = wxString::Format("file://%s/web/model/index.html?lang=%s", from_u8(resources_dir()), strlang); + + wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); + + m_browser = WebView::CreateWebView(this, m_project_home_url); + if (m_browser == nullptr) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("load web view of project page failed"); + return; + } + //m_browser->Hide(); + main_sizer->Add(m_browser, wxSizerFlags().Expand().Proportion(1)); + m_browser->Bind(wxEVT_WEBVIEW_NAVIGATED, &ProjectPanel::on_navigated, this); + m_browser->Bind(wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED, &ProjectPanel::OnScriptMessage, this, m_browser->GetId()); + + Bind(EVT_PROJECT_RELOAD, &ProjectPanel::on_reload, this); + + SetSizer(main_sizer); + Layout(); + Fit(); +} + +ProjectPanel::~ProjectPanel() {} + +void ProjectPanel::on_reload(wxCommandEvent& evt) +{ + std::string update_type; + std::string license; + std::string model_name; + std::string model_author; + std::string cover_file; + std::string description; + std::map> files; + + std::string p_name; + std::string p_author; + std::string p_description; + std::string p_cover_file; + + Model model = wxGetApp().plater()->model(); + + license = model.model_info->license; + model_name = model.model_info->model_name; + cover_file = model.model_info->cover_file; + description = model.model_info->description; + update_type = model.model_info->origin; + + + try { + if (!model.model_info->copyright.empty()) { + json copy_right = json::parse(model.model_info->copyright); + + if (copy_right.is_array()) { + for (auto it = copy_right.begin(); it != copy_right.end(); it++) { + if ((*it).contains("author")) { + model_author = (*it)["author"].get(); + } + } + } + } + } + catch (...) { + ; + } + + if (model_author.empty() && model.design_info != nullptr) + model_author = model.design_info->Designer; + + if (model.profile_info != nullptr) { + p_name = model.profile_info->ProfileTile; + p_description = model.profile_info->ProfileDescription; + p_cover_file = model.profile_info->ProfileCover; + p_author = model.profile_info->ProfileUserName; + } + + //file info + std::string file_path = encode_path(wxGetApp().plater()->model().get_auxiliary_file_temp_path().c_str()); + if (!file_path.empty()) { + files = Reload(file_path); + } + else { + clear_model_info(); + return; + } + + json j; + j["model"]["license"] = license; + j["model"]["name"] = wxGetApp().url_encode(model_name); + j["model"]["author"] = wxGetApp().url_encode(model_author);; + j["model"]["cover_img"] = wxGetApp().url_encode(cover_file); + j["model"]["description"] = wxGetApp().url_encode(description); + j["model"]["preview_img"] = files["Model Pictures"]; + j["model"]["upload_type"] = update_type; + + j["file"]["BOM"] = files["Bill of Materials"]; + j["file"]["Assembly"] = files["Assembly Guide"]; + j["file"]["Other"] = files["Others"]; + + j["profile"]["name"] = wxGetApp().url_encode(p_name); + j["profile"]["author"] = wxGetApp().url_encode(p_author); + j["profile"]["description"] = wxGetApp().url_encode(p_description); + j["profile"]["cover_img"] = wxGetApp().url_encode(p_cover_file); + j["profile"]["preview_img"] = files["Profile Pictures"]; + + json m_Res = json::object(); + m_Res["command"] = "show_3mf_info"; + m_Res["sequence_id"] = std::to_string(ProjectPanel::m_sequence_id++); + m_Res["model"] = j; + + wxString strJS = wxString::Format("HandleStudio(%s)", m_Res.dump(-1, ' ', false, json::error_handler_t::ignore)); + + if (m_web_init_completed) { + wxGetApp().CallAfter([this, strJS] { + RunScript(strJS.ToStdString()); + }); + } +} + +void ProjectPanel::msw_rescale() +{ +} + +void ProjectPanel::on_size(wxSizeEvent &event) +{ + event.Skip(); +} + +void ProjectPanel::on_navigated(wxWebViewEvent& event) +{ + event.Skip(); +} + +void ProjectPanel::OnScriptMessage(wxWebViewEvent& evt) +{ + try { + wxString strInput = evt.GetString(); + json j = json::parse(strInput); + + wxString strCmd = j["command"]; + + if (strCmd == "open_3mf_accessory") { + wxString accessory_path = j["accessory_path"]; + + if (!accessory_path.empty()) { + std::string decode_path = wxGetApp().url_decode(accessory_path.ToStdString()); + fs::path path(decode_path); + + if (fs::exists(path)) { + wxLaunchDefaultApplication(path.wstring(), 0); + } + } + } + else if (strCmd == "request_3mf_info") { + m_web_init_completed = true; + } + else if (strCmd == "debug_info") { + //wxString msg = j["msg"]; + //OutputDebugString(wxString::Format("Model_Web: msg = %s \r\n", msg)); + //BOOST_LOG_TRIVIAL(info) << wxString::Format("Model_Web: msg = %s", msg); + } + + } + catch (std::exception& e) { + // wxMessageBox(e.what(), "json Exception", MB_OK); + } +} + +void ProjectPanel::update_model_data() +{ + Model model = wxGetApp().plater()->model(); + clear_model_info(); + + //basics info + if (model.model_info == nullptr) + return; + + auto event = wxCommandEvent(EVT_PROJECT_RELOAD); + event.SetEventObject(this); + wxPostEvent(this, event); +} + +void ProjectPanel::clear_model_info() +{ + json m_Res = json::object(); + m_Res["command"] = "clear_3mf_info"; + m_Res["sequence_id"] = std::to_string(ProjectPanel::m_sequence_id++); + + wxString strJS = wxString::Format("HandleStudio(%s)", m_Res.dump(-1, ' ', false, json::error_handler_t::ignore)); + + wxGetApp().CallAfter([this, strJS] { + RunScript(strJS.ToStdString()); + }); +} + +std::map> ProjectPanel::Reload(wxString aux_path) +{ + std::vector dir_cache; + fs::directory_iterator iter_end; + wxString m_root_dir; + std::map> m_paths_list; + + const static std::array s_default_folders = { + ("Model Pictures"), + ("Bill of Materials"), + ("Assembly Guide"), + ("Others"), + //(".thumbnails"), + ("Profile Pictures"), + }; + + for (auto folder : s_default_folders) + m_paths_list[folder.ToStdString()] = std::vector{}; + + + fs::path new_aux_path(aux_path.ToStdWstring()); + + try { + fs::remove_all(fs::path(m_root_dir.ToStdWstring())); + } + catch (...) { + BOOST_LOG_TRIVIAL(error) << "Failed removing the auxiliary directory" << m_root_dir.c_str(); + } + + m_root_dir = aux_path; + // Check new path. If not exist, create a new one. + if (!fs::exists(new_aux_path)) { + fs::create_directory(new_aux_path); + // Create default folders if they are not loaded + for (auto folder : s_default_folders) { + wxString folder_path = aux_path + "/" + folder; + if (fs::exists(folder_path.ToStdWstring())) continue; + fs::create_directory(folder_path.ToStdWstring()); + } + return m_paths_list; + } + + // Load from new path + for (fs::directory_iterator iter(new_aux_path); iter != iter_end; iter++) { + wxString path = iter->path().generic_wstring(); + dir_cache.push_back(iter->path()); + } + + + for (auto dir : dir_cache) { + for (fs::directory_iterator iter(dir); iter != iter_end; iter++) { + if (fs::is_directory(iter->path())) continue; + + json pfile_obj; + + std::string file_path = iter->path().string(); + fs::path file_path_obj = fs::path(iter->path().string()); + + for (auto folder : s_default_folders) { + auto idx = file_path.find(folder.ToStdString()); + if (idx != std::string::npos) { + + wxStructStat strucStat; + wxString file_name = encode_path(file_path.c_str()); + wxStat(file_name, &strucStat); + wxFileOffset filelen = strucStat.st_size; + + pfile_obj["filename"] = wxGetApp().url_encode(file_path_obj.filename().string().c_str()); + pfile_obj["size"] = formatBytes((unsigned long)filelen); + + //image + if (file_path_obj.extension() == ".jpg" || + file_path_obj.extension() == ".jpeg" || + file_path_obj.extension() == ".png" || + file_path_obj.extension() == ".bmp") + { + + wxString base64_str = to_base64(file_path); + pfile_obj["filepath"] = base64_str.ToStdString(); + m_paths_list[folder.ToStdString()].push_back(pfile_obj); + break; + } + else { + pfile_obj["filepath"] = wxGetApp().url_encode(file_path); + m_paths_list[folder.ToStdString()].push_back(pfile_obj); + break; + } + } + } + } + } + + return m_paths_list; +} + +std::string ProjectPanel::formatBytes(unsigned long bytes) +{ + double dValidData = round(double(bytes) / (1024 * 1024) * 1000) / 1000; + return wxString::Format("%.2fMB", dValidData).ToStdString(); +} + +wxString ProjectPanel::to_base64(std::string file_path) +{ + std::map base64_format; + base64_format[".jpg"] = wxBITMAP_TYPE_JPEG; + base64_format[".jpeg"] = wxBITMAP_TYPE_JPEG; + base64_format[".png"] = wxBITMAP_TYPE_PNG; + base64_format[".bmp"] = wxBITMAP_TYPE_BMP; + + std::string extension = file_path.substr(file_path.rfind("."), file_path.length()); + + auto image = new wxImage(encode_path(file_path.c_str())); + wxMemoryOutputStream mem; + image->SaveFile(mem, base64_format[extension]); + + wxString km = wxBase64Encode(mem.GetOutputStreamBuffer()->GetBufferStart(), + mem.GetSize()); + + std::wstringstream wss; + wss << L"data:image/jpg;base64,"; + //wss << wxBase64Encode(km.data(), km.size()); + wss << km; + + wxString base64_str = wss.str(); + return base64_str; +} + +void ProjectPanel::RunScript(std::string content) +{ + WebView::RunScript(m_browser, content); +} + +bool ProjectPanel::Show(bool show) +{ + if (show) update_model_data(); + return wxPanel::Show(show); +} + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Project.hpp b/src/slic3r/GUI/Project.hpp new file mode 100644 index 0000000000..952f05d2c5 --- /dev/null +++ b/src/slic3r/GUI/Project.hpp @@ -0,0 +1,96 @@ +#ifndef slic3r_Project_hpp_ +#define slic3r_Project_hpp_ + +#include "Tabbook.hpp" +#include "wx/artprov.h" +#include "wx/cmdline.h" +#include "wx/notifmsg.h" +#include "wx/settings.h" +#include "wx/webview.h" + +#if wxUSE_WEBVIEW_EDGE +#include "wx/msw/webview_edge.h" +#endif + +#include "wx/numdlg.h" +#include "wx/infobar.h" +#include "wx/filesys.h" +#include "wx/fs_arc.h" +#include "wx/fs_mem.h" +#include "wx/stdpaths.h" +#include +#include +#include "wx/textctrl.h" +#include + +#include "nlohmann/json.hpp" +#include "slic3r/Utils/json_diff.hpp" + +#include +#include +#include +#include "Event.hpp" +#include "libslic3r/ProjectTask.hpp" +#include "wxExtensions.hpp" + +#define AUFILE_GREY700 wxColour(107, 107, 107) +#define AUFILE_GREY500 wxColour(158, 158, 158) +#define AUFILE_GREY300 wxColour(238, 238, 238) +#define AUFILE_GREY200 wxColour(248, 248, 248) +#define AUFILE_BRAND wxColour(0, 174, 66) +#define AUFILE_BRAND_TRANSPARENT wxColour(215, 232, 222) +//#define AUFILE_PICTURES_SIZE wxSize(FromDIP(300), FromDIP(300)) +//#define AUFILE_PICTURES_PANEL_SIZE wxSize(FromDIP(300), FromDIP(340)) +#define AUFILE_PICTURES_SIZE wxSize(FromDIP(168), FromDIP(168)) +#define AUFILE_PICTURES_PANEL_SIZE wxSize(FromDIP(168), FromDIP(208)) +#define AUFILE_SIZE wxSize(FromDIP(168), FromDIP(168)) +#define AUFILE_PANEL_SIZE wxSize(FromDIP(168), FromDIP(208)) +#define AUFILE_TEXT_HEIGHT FromDIP(40) +#define AUFILE_ROUNDING FromDIP(5) + +namespace Slic3r { namespace GUI { + +struct project_file{ + std::string filepath; + std::string filename; + std::string size; +}; + +class ProjectPanel : public wxPanel +{ +private: + bool m_web_init_completed = {false}; + bool m_reload_already = {false}; + + wxWebView* m_browser = {nullptr}; + wxString m_project_home_url; + wxString m_root_dir; + static inline int m_sequence_id = 8000; + + +public: + ProjectPanel(wxWindow *parent, wxWindowID id = wxID_ANY, const wxPoint &pos = wxDefaultPosition, const wxSize &size = wxDefaultSize, long style = wxTAB_TRAVERSAL); + ~ProjectPanel(); + + + void on_reload(wxCommandEvent& evt); + void on_size(wxSizeEvent &event); + void on_navigated(wxWebViewEvent& event); + + void msw_rescale(); + void update_model_data(); + void clear_model_info(); + + bool Show(bool show); + void OnScriptMessage(wxWebViewEvent& evt); + void RunScript(std::string content); + + std::map> Reload(wxString aux_path); + std::string formatBytes(unsigned long bytes); + wxString to_base64(std::string path); +}; + +wxDECLARE_EVENT(EVT_PROJECT_RELOAD, wxCommandEvent); +}} // namespace Slic3r::GUI + +#endif diff --git a/src/slic3r/GUI/SavePresetDialog.cpp b/src/slic3r/GUI/SavePresetDialog.cpp index 49a4c4a9a1..e3994b4813 100644 --- a/src/slic3r/GUI/SavePresetDialog.cpp +++ b/src/slic3r/GUI/SavePresetDialog.cpp @@ -250,6 +250,9 @@ void SavePresetDialog::Item::update() } else { m_radio_user->Enable(); m_radio_project->Enable(); + + m_radio_user->SetValue(!m_save_to_project); + m_radio_project->SetValue(m_save_to_project); } m_valid_label->SetLabel(info_line); diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp index f4965cb9f5..c7c0978b97 100644 --- a/src/slic3r/GUI/Search.cpp +++ b/src/slic3r/GUI/Search.cpp @@ -366,7 +366,11 @@ void OptionsSearcher::show_dialog(Preset::Type type, wxWindow *parent, TextInput if (parent == nullptr || input == nullptr) return; auto search_dialog = new SearchDialog(this, type, parent, input, ssearch_btn); wxPoint pos = input->GetParent()->ClientToScreen(wxPoint(0, 0)); +#ifndef __WXGTK__ pos.y += input->GetParent()->GetRect().height; +#else + input->GetParent()->Hide(); +#endif search_dialog->SetPosition(pos); search_dialog->Popup(); } @@ -558,11 +562,18 @@ SearchDialog::SearchDialog(OptionsSearcher *searcher, Preset::Type type, wxWindo // search line //search_line = new wxTextCtrl(m_client_panel, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER); +#ifdef __WXGTK__ + search_line = new TextInput(m_client_panel, wxEmptyString, wxEmptyString, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + search_line->SetBackgroundColour(wxColour(238, 238, 238)); + search_line->SetForegroundColour(wxColour(43, 52, 54)); + search_line->SetFont(GUI::wxGetApp().bold_font()); +#endif // default_string = _L("Enter a search term"); search_line->Bind(wxEVT_TEXT, &SearchDialog::OnInputText, this); search_line->Bind(wxEVT_LEFT_UP, &SearchDialog::OnLeftUpInTextCtrl, this); search_line->Bind(wxEVT_KEY_DOWN, &SearchDialog::OnKeyDown, this); + search_line2 = search_line->GetTextCtrl(); // scroll window m_scrolledWindow = new ScrolledWindow(m_client_panel, wxID_ANY, wxDefaultPosition, wxSize(POPUP_WIDTH * em - (em + em /2), POPUP_HEIGHT * em), wxVSCROLL, 6, 6); @@ -579,7 +590,10 @@ SearchDialog::SearchDialog(OptionsSearcher *searcher, Preset::Type type, wxWindo m_listPanel->Fit(); m_scrolledWindow->SetScrollbars(1, 1, 0, m_listPanel->GetSize().GetHeight()); - //m_sizer_body->Add(search_line, 0, wxEXPAND | wxTOP, em + em / 2); +#ifdef __WXGTK__ + m_sizer_body->Add(search_line, 0, wxEXPAND | wxALL, em / 2); + search_line = input; +#endif m_sizer_body->Add(m_scrolledWindow, 0, wxEXPAND | wxALL, em); m_client_panel->SetSizer(m_sizer_body); @@ -589,7 +603,7 @@ SearchDialog::SearchDialog(OptionsSearcher *searcher, Preset::Type type, wxWindo m_border_panel->SetSizer(m_sizer_main); m_border_panel->Layout(); - m_sizer_border->Add(m_border_panel, 0, wxEXPAND | wxALL, 1); + m_sizer_border->Add(m_border_panel, 1, wxEXPAND | wxALL, 1); SetSizer(m_sizer_border); Layout(); @@ -619,11 +633,11 @@ void SearchDialog::Popup(wxPoint position /*= wxDefaultPosition*/) //const std::string &line = searcher->search_string(); //search_line->SetValue(line.empty() ? default_string : from_u8(line)); - search_line->GetTextCtrl()->SetValue(wxString("")); + search_line2->SetValue(wxString("")); //const std::string &line = searcher->search_string(); //searcher->search(into_u8(line), true); PopupWindow::Popup(); - search_line->SetFocus(); + search_line2->SetFocus(); update_list(); } @@ -671,8 +685,8 @@ void SearchDialog::ProcessSelection(wxDataViewItem selection) void SearchDialog::OnInputText(wxCommandEvent &) { - search_line->GetTextCtrl()->SetInsertionPointEnd(); - wxString input_string = search_line->GetTextCtrl()->GetValue(); + search_line2->SetInsertionPointEnd(); + wxString input_string = search_line2->GetValue(); if (input_string == default_string) input_string.Clear(); searcher->search(into_u8(input_string), true, search_type); update_list(); @@ -680,7 +694,7 @@ void SearchDialog::OnInputText(wxCommandEvent &) void SearchDialog::OnLeftUpInTextCtrl(wxEvent &event) { - if (search_line->GetTextCtrl()->GetValue() == default_string) search_line->GetTextCtrl()->SetValue(""); + if (search_line2->GetValue() == default_string) search_line2->SetValue(""); event.Skip(); } @@ -726,7 +740,9 @@ void SearchDialog::OnSelect(wxDataViewEvent &event) void SearchDialog::update_list() { +#ifndef __WXGTK__ Freeze(); +#endif m_scrolledWindow->Destroy(); m_scrolledWindow = new ScrolledWindow(m_client_panel, wxID_ANY, wxDefaultPosition, wxSize(POPUP_WIDTH * em - (em + em / 2), POPUP_HEIGHT * em - em), wxVSCROLL, 6, 6); @@ -755,7 +771,9 @@ void SearchDialog::update_list() m_sizer_body->Add(m_scrolledWindow, 0, wxEXPAND | wxALL, em); m_sizer_body->Fit(m_client_panel); m_sizer_body->Layout(); +#ifndef __WXGTK__ Thaw(); +#endif // Under OSX model->Clear invoke wxEVT_DATAVIEW_SELECTION_CHANGED, so // set prevent_list_events to true already here diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp index 6331609ca5..51f5030428 100644 --- a/src/slic3r/GUI/Search.hpp +++ b/src/slic3r/GUI/Search.hpp @@ -206,7 +206,8 @@ public: const int POPUP_WIDTH = 38; const int POPUP_HEIGHT = 40; - TextInput * search_line{nullptr}; + TextInput * search_line{nullptr}; + wxTextCtrl * search_line2{nullptr}; Preset::Type search_type = Preset::TYPE_INVALID; wxDataViewCtrl * search_list{nullptr}; diff --git a/src/slic3r/GUI/SelectMachine.cpp b/src/slic3r/GUI/SelectMachine.cpp index 581132b1b1..64d4d279d7 100644 --- a/src/slic3r/GUI/SelectMachine.cpp +++ b/src/slic3r/GUI/SelectMachine.cpp @@ -1022,6 +1022,11 @@ SelectMachineDialog::SelectMachineDialog(Plater *plater) m_rename_input->SetMinSize(wxSize(FromDIP(380), FromDIP(24))); m_rename_input->SetMaxSize(wxSize(FromDIP(380), FromDIP(24))); m_rename_input->Bind(wxEVT_TEXT_ENTER, [this](auto& e) {on_rename_enter();}); + m_rename_input->Bind(wxEVT_KILL_FOCUS, [this](auto& e) { + if (!m_rename_input->HasFocus() && !m_rename_text->HasFocus()) + on_rename_enter(); + else + e.Skip(); }); rename_edit_sizer_v->Add(m_rename_input, 1, wxALIGN_CENTER, 0); @@ -1079,8 +1084,7 @@ SelectMachineDialog::SelectMachineDialog(Plater *plater) m_sizer_basic_time->Add(m_stext_weight, 0, wxALL, FromDIP(5)); m_sizer_basic->Add(m_sizer_basic_time, 0, wxALIGN_CENTER, 0); - auto m_sizer_material_area = new wxBoxSizer(wxHORIZONTAL); - + wxBoxSizer* m_sizer_material_area = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer* m_sizer_material_tips = new wxBoxSizer(wxHORIZONTAL); @@ -1112,6 +1116,29 @@ SelectMachineDialog::SelectMachineDialog(Plater *plater) m_sizer_material_area->Add(m_sizer_material_tips, 0, wxALIGN_CENTER|wxLEFT, FromDIP(8)); m_sizer_material_area->Add(m_sizer_material, 0, wxLEFT, FromDIP(15)); +#ifdef FILAMENT_BACKUP + auto m_sizer_backup = new wxBoxSizer(wxHORIZONTAL); + auto m_ams_backup_tip = new Label(this, _L("Ams filament backup")); + m_ams_backup_tip->SetFont(::Label::Head_12); + m_ams_backup_tip->SetForegroundColour(wxColour(0x00AE42)); + m_ams_backup_tip->SetBackgroundColour(*wxWHITE); + auto img_ams_backup = new wxStaticBitmap(this, wxID_ANY, create_scaled_bitmap("automatic_material_renewal", this, 16), wxDefaultPosition, wxSize(FromDIP(16), FromDIP(16)), 0); + img_ams_backup->SetBackgroundColour(*wxWHITE); + + m_sizer_backup->Add(0, 0, 1, wxEXPAND, 0); + m_sizer_backup->Add(img_ams_backup, 0, wxALL, FromDIP(3)); + m_sizer_backup->Add(m_ams_backup_tip, 0, wxTOP, FromDIP(5)); + + m_ams_backup_tip->Bind(wxEVT_ENTER_WINDOW, [this, img_amsmapping_tip](auto& e) {SetCursor(wxCURSOR_HAND); }); + img_ams_backup->Bind(wxEVT_ENTER_WINDOW, [this, img_amsmapping_tip](auto& e) {SetCursor(wxCURSOR_HAND); }); + + m_ams_backup_tip->Bind(wxEVT_LEAVE_WINDOW, [this](auto& e) {SetCursor(wxCURSOR_ARROW); }); + img_ams_backup->Bind(wxEVT_LEAVE_WINDOW, [this](auto& e) {SetCursor(wxCURSOR_ARROW); }); + + m_ams_backup_tip->Bind(wxEVT_LEFT_DOWN, [this](auto& e) {popup_filament_backup(); on_rename_enter(); }); + img_ams_backup->Bind(wxEVT_LEFT_DOWN, [this](auto& e) {popup_filament_backup(); }); +#endif + m_statictext_ams_msg = new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL); m_statictext_ams_msg->SetFont(::Label::Body_13); m_statictext_ams_msg->Hide(); @@ -1197,8 +1224,6 @@ SelectMachineDialog::SelectMachineDialog(Plater *plater) m_sizer_prepare->Add(hyperlink_sizer, 0, wxALIGN_CENTER | wxALL, 5); - - m_sizer_pcont->Add(0, 0, 1, wxEXPAND, 0); m_button_ensure = new Button(m_panel_prepare, _L("Send")); m_button_ensure->SetBackgroundColor(btn_bg_enable); m_button_ensure->SetBorderColor(btn_bg_enable); @@ -1206,9 +1231,12 @@ SelectMachineDialog::SelectMachineDialog(Plater *plater) m_button_ensure->SetSize(SELECT_MACHINE_DIALOG_BUTTON_SIZE); m_button_ensure->SetMinSize(SELECT_MACHINE_DIALOG_BUTTON_SIZE); m_button_ensure->SetCornerRadius(FromDIP(12)); - m_button_ensure->Bind(wxEVT_BUTTON, &SelectMachineDialog::on_ok_btn, this); + + m_sizer_pcont->Add(0, 0, 1, wxEXPAND, 0); m_sizer_pcont->Add(m_button_ensure, 0, wxEXPAND | wxBOTTOM, FromDIP(10)); + + m_sizer_prepare->Add(m_sizer_pcont, 0, wxEXPAND, 0); m_panel_prepare->SetSizer(m_sizer_prepare); m_panel_prepare->Layout(); @@ -1291,6 +1319,11 @@ SelectMachineDialog::SelectMachineDialog(Plater *plater) m_sizer_main->Add(m_line_top, 0, wxEXPAND, 0); m_sizer_main->Add(0, 0, 0, wxTOP, FromDIP(22)); m_sizer_main->Add(m_scrollable_view, 0, wxALIGN_CENTER_HORIZONTAL, 0); + +#ifdef FILAMENT_BACKUP + m_sizer_main->Add(m_sizer_backup, 0, wxALIGN_CENTER_HORIZONTAL, 0); +#endif + m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(8)); m_sizer_main->Add(m_statictext_ams_msg, 0, wxALIGN_CENTER_HORIZONTAL, 0); m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(8)); @@ -1335,6 +1368,17 @@ void SelectMachineDialog::check_fcous_state(wxWindow* window) } } +void SelectMachineDialog::popup_filament_backup() +{ + DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager(); + if (!dev) return; + if (dev->get_selected_machine() && dev->get_selected_machine()->filam_bak.size() > 0) { + AmsReplaceMaterialDialog* m_replace_material_popup = new AmsReplaceMaterialDialog(this); + m_replace_material_popup->update_machine_obj(dev->get_selected_machine()); + m_replace_material_popup->ShowModal(); + } +} + wxWindow *SelectMachineDialog::create_ams_checkbox(wxString title, wxWindow *parent, wxString tooltip) { auto checkbox = new wxWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); @@ -1354,7 +1398,7 @@ wxWindow *SelectMachineDialog::create_ams_checkbox(wxString title, wxWindow *par text->SetFont(::Label::Body_13); text->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#323A3C"))); text->Wrap(-1); - sizer_checkbox->Add(text, 0, wxALIGN_CENTER, FromDIP(5)); + sizer_checkbox->Add(text, 0, wxALIGN_CENTER, 0); auto img_ams_tip = new wxStaticBitmap(checkbox, wxID_ANY, create_scaled_bitmap("enable_ams", this, 16), wxDefaultPosition, wxSize(FromDIP(16), FromDIP(16)), 0); sizer_checkbox->Add(img_ams_tip, 0, wxALIGN_CENTER | wxLEFT, FromDIP(5)); @@ -1387,8 +1431,12 @@ wxWindow *SelectMachineDialog::create_ams_checkbox(wxString title, wxWindow *par wxGetApp().app_config->set("bbl_machine", "use_ams", ams_check->GetValue()); }); - text->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent & event) { - ams_check->SetValue(ams_check->GetValue() ? false : true); + text->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent& event) { + ams_check->SetValue(ams_check->GetValue() ? false : true); + }); + + checkbox->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent& event) { + ams_check->SetValue(ams_check->GetValue() ? false : true); }); return checkbox; } @@ -1414,7 +1462,7 @@ wxWindow *SelectMachineDialog::create_item_checkbox(wxString title, wxWindow *pa text->Wrap(-1); text->SetMinSize(wxSize(FromDIP(120), -1)); text->SetMaxSize(wxSize(FromDIP(120), -1)); - sizer_checkbox->Add(text, 0, wxBOTTOM | wxEXPAND | wxTOP, FromDIP(5)); + sizer_checkbox->Add(text, 0, wxALIGN_CENTER, 0); checkbox->SetSizer(sizer_checkbox); checkbox->Layout(); @@ -1428,6 +1476,40 @@ wxWindow *SelectMachineDialog::create_item_checkbox(wxString title, wxWindow *pa }); text->Bind(wxEVT_LEFT_DOWN, [this, check](wxMouseEvent &) { check->SetValue(check->GetValue() ? false : true); }); + + + check->Bind(wxEVT_LEFT_DOWN, [this, check, param](wxMouseEvent &e) { + AppConfig* config = wxGetApp().app_config; + if (config) { + if (check->GetValue()) + config->set_str("print", param, "0"); + else + config->set_str("print", param, "1"); + } + e.Skip(); + }); + + checkbox->Bind(wxEVT_LEFT_DOWN, [this, check, param](wxMouseEvent&) { + check->SetValue(check->GetValue() ? false : true); + AppConfig* config = wxGetApp().app_config; + if (config) { + if (check->GetValue()) + config->set_str("print", param, "1"); + else + config->set_str("print", param, "0"); + } + }); + + text->Bind(wxEVT_LEFT_DOWN, [this, check, param](wxMouseEvent &) { + check->SetValue(check->GetValue() ? false : true); + AppConfig* config = wxGetApp().app_config; + if (config) { + if (check->GetValue()) + config->set_str("print", param, "1"); + else + config->set_str("print", param, "0"); + } + }); m_checkbox_list[param] = check; return checkbox; } @@ -1469,6 +1551,9 @@ void SelectMachineDialog::update_select_layout(MachineObject *obj) void SelectMachineDialog::prepare_mode() { + // disable combobox + m_comboBox_printer->Enable(); + m_is_in_sending_mode = false; if (m_print_job) { m_print_job->join(); @@ -1489,6 +1574,9 @@ void SelectMachineDialog::prepare_mode() void SelectMachineDialog::sending_mode() { + // disable combobox + m_comboBox_printer->Disable(); + m_is_in_sending_mode = true; if (m_simplebook->GetSelection() != 1){ m_simplebook->SetSelection(1); @@ -2003,13 +2091,13 @@ void SelectMachineDialog::on_ok_btn(wxCommandEvent &event) std::vector confirm_text; - confirm_text.push_back(_L("Please check the following infomation and click Confirm to continue sending print:") + "\n"); + confirm_text.push_back(_L("Please check the following:") + "\n\n"); -#if 0 +#if 1 //Check Printer Model Id bool is_same_printer_type = is_same_printer_model(); if (!is_same_printer_type) - confirm_text.push_back(_L("The printer type used to generate G-code is not the same type as the currently selected physical printer. It is recommend to re-slice by selecting the same printer type.") + "\n"); + confirm_text.push_back(_L("The printer type selected when generating G-Code is not consistent with the currently selected printer. It is recommended that you use the same printer type for slicing.") + "\n"); #else bool is_same_printer_type = true; #endif @@ -2041,14 +2129,34 @@ void SelectMachineDialog::on_ok_btn(wxCommandEvent &event) auto mapping_result = m_mapping_popup.parse_ams_mapping(obj_->amsList); auto has_unknown_filament = false; // check if ams mapping is has errors, tpu - bool has_tpu_filament = false; + bool has_prohibited_filament = false; + wxString prohibited_error = wxEmptyString; + for (auto i = 0; i < m_ams_mapping_result.size(); i++) { + auto tid = m_ams_mapping_result[i].tray_id; + std::string filament_type = boost::to_upper_copy(m_ams_mapping_result[i].type); - if (filament_type == "TPU") { - has_tpu_filament = true; + std::string filament_brand; + + for (auto fs : m_filaments) { + if (fs.id == m_ams_mapping_result[i].id) { + filament_brand = boost::to_upper_copy(m_filaments[i].brand); + } } + + if (filament_type == "TPU") { + has_prohibited_filament = true; + prohibited_error = wxString::Format(_L("%s is not supported by AMS."), "TPU"); + }else if (filament_type == "PET-CF" && filament_brand == "BAMBULAB") { + has_prohibited_filament = true; + prohibited_error = wxString::Format(_L("%s is not supported by AMS."), "PET-CF"); + }else if (filament_type == "PA6-CF" && filament_brand == "BAMBULAB") { + has_prohibited_filament = true; + prohibited_error = wxString::Format(_L("%s is not supported by AMS."), "PA6-CF"); + } + for (auto miter : mapping_result) { //matching if (miter.id == tid) { @@ -2060,8 +2168,8 @@ void SelectMachineDialog::on_ok_btn(wxCommandEvent &event) } } - if (has_tpu_filament && obj_->has_ams() && ams_check->GetValue()) { - wxString tpu_tips = wxString::Format(_L("The %s filament is too soft to be used with the AMS"), "TPU"); + if (has_prohibited_filament && obj_->has_ams() && ams_check->GetValue()) { + wxString tpu_tips = prohibited_error; show_errors(tpu_tips); return; } @@ -2078,14 +2186,19 @@ void SelectMachineDialog::on_ok_btn(wxCommandEvent &event) confirm_dlg.on_hide(); this->on_ok(); }); + + confirm_text.push_back(_L("Please click the confirm button if you still want to proceed with printing.") + "\n"); wxString info_msg = wxEmptyString; for (auto i = 0; i < confirm_text.size(); i++) { if (i == 0) { info_msg += confirm_text[i]; } + else if (i == confirm_text.size() - 1) { + info_msg += confirm_text[i]; + } else { - info_msg += wxString::Format("%d:%s\n",i, confirm_text[i]); + info_msg += wxString::Format("%d. %s\n",i, confirm_text[i]); } } @@ -2244,6 +2357,11 @@ void SelectMachineDialog::on_ok() wxGetApp().show_ip_address_enter_dialog(); }); + // update ota version + NetworkAgent* agent = wxGetApp().getAgent(); + if (agent) + agent->track_update_property("dev_ota_version", obj_->get_ota_version()); + m_print_job->start(); BOOST_LOG_TRIVIAL(info) << "print_job: start print job"; } @@ -2591,7 +2709,7 @@ void SelectMachineDialog::on_selection_changed(wxCommandEvent &event) if (obj && !obj->get_lan_mode_connection_state()) { obj->command_get_version(); obj->command_request_push_all(); - dev->set_selected_machine(m_printer_last_select); + dev->set_selected_machine(m_printer_last_select, true); // Has changed machine unrecoverably GUI::wxGetApp().sidebar().load_ams_list(obj->dev_id, obj->amsList); update_select_layout(obj); @@ -2696,17 +2814,26 @@ void SelectMachineDialog::update_show_status() // do ams mapping if no ams result + bool clean_ams_mapping = false; if (obj_->has_ams() && m_ams_mapping_result.empty()) { if (obj_->ams_support_use_ams) { if (ams_check->GetValue()) { do_ams_mapping(obj_); } else { - m_ams_mapping_result.clear(); - sync_ams_mapping_result(m_ams_mapping_result); + clean_ams_mapping = true; } } } + if (!obj_->has_ams() || !ams_check->GetValue()) { + clean_ams_mapping = true; + } + + if (clean_ams_mapping) { + m_ams_mapping_result.clear(); + sync_ams_mapping_result(m_ams_mapping_result); + } + // reading done if (wxGetApp().app_config && wxGetApp().app_config->get("internal_debug").empty()) { if (obj_->upgrade_force_upgrade) { @@ -2746,7 +2873,7 @@ void SelectMachineDialog::update_show_status() } // no ams - if (!obj_->has_ams()) { + if (!obj_->has_ams() || !ams_check->GetValue()) { if (!has_tips(obj_)) show_status(PrintDialogStatus::PrintStatusReadingFinished); return; @@ -2969,10 +3096,23 @@ void SelectMachineDialog::set_default() select_bed->Show(); select_flow->Show(); - // checkbox default values - m_checkbox_list["bed_leveling"]->SetValue(true); - m_checkbox_list["flow_cali"]->SetValue(true); - m_checkbox_list["timelapse"]->SetValue(true); + // load checkbox values from app config + AppConfig* config = wxGetApp().app_config; + if (config && config->get("print", "bed_leveling") == "0") { + m_checkbox_list["bed_leveling"]->SetValue(false); + } else { + m_checkbox_list["bed_leveling"]->SetValue(true); + } + if (config && config->get("print", "flow_cali") == "0") { + m_checkbox_list["flow_cali"]->SetValue(false); + } else { + m_checkbox_list["flow_cali"]->SetValue(true); + } + if (config && config->get("print", "timelapse") == "0") { + m_checkbox_list["timelapse"]->SetValue(false); + } else { + m_checkbox_list["timelapse"]->SetValue(true); + } ams_check->SetValue(true); // thumbmail @@ -2998,6 +3138,7 @@ void SelectMachineDialog::set_default() //sizer_thumbnail->Layout(); std::vector materials; + std::vector brands; std::vector display_materials; { auto preset_bundle = wxGetApp().preset_bundle; @@ -3008,6 +3149,11 @@ void SelectMachineDialog::set_default() std::string filament_type = iter->config.get_filament_type(display_filament_type); display_materials.push_back(display_filament_type); materials.push_back(filament_type); + + if (iter->vendor && !iter->vendor->name.empty()) + brands.push_back(iter->vendor->name); + else + brands.push_back(""); } } } @@ -3081,9 +3227,14 @@ void SelectMachineDialog::set_default() if (m_mapping_popup.IsShown()) return; wxPoint pos = item->ClientToScreen(wxPoint(0, 0)); pos.y += item->GetRect().height; - m_mapping_popup.Position(pos, wxSize(0, 0)); + m_mapping_popup.Move(pos); - if (obj_ && obj_->has_ams() && ams_check->GetValue()) { + if (obj_ && + obj_->has_ams() && + ams_check->GetValue() && + obj_->dev_id == m_printer_last_select) + { + m_mapping_popup.set_parent_item(item); m_mapping_popup.set_current_filament_id(extruder); m_mapping_popup.set_tag_texture(materials[extruder]); m_mapping_popup.update_ams_data(obj_->amsList); @@ -3102,6 +3253,7 @@ void SelectMachineDialog::set_default() FilamentInfo info; info.id = extruder; info.type = materials[extruder]; + info.brand = brands[extruder]; info.color = colour_rgb.GetAsString(wxC2S_HTML_SYNTAX).ToStdString(); m_filaments.push_back(info); } @@ -3153,7 +3305,7 @@ void SelectMachineDialog::set_default() wxString time; PartPlate *plate = m_plater->get_partplate_list().get_curr_plate(); if (plate) { - if (plate->get_slice_result()) { time = wxString::Format("%s", get_bbl_monitor_time_dhm(plate->get_slice_result()->print_statistics.modes[0].time)); } + if (plate->get_slice_result()) { time = wxString::Format("%s", short_time(get_time_dhms(plate->get_slice_result()->print_statistics.modes[0].time))); } } char weight[64]; @@ -3163,6 +3315,17 @@ void SelectMachineDialog::set_default() m_stext_weight->SetLabel(weight); } +void SelectMachineDialog::sys_color_changed() +{ + if (wxGetApp(). dark_mode()) { + m_rename_button->SetIcon("ams_editable_light"); + } + else { + m_rename_button->SetIcon("ams_editable"); + } + m_rename_button->Refresh(); +} + bool SelectMachineDialog::Show(bool show) { show_status(PrintDialogStatus::PrintStatusInit); @@ -3381,7 +3544,7 @@ void EditDevNameDialog::on_edit_name(wxCommandEvent &e) SetBackgroundStyle(wxBG_STYLE_CUSTOM); wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL); - m_staticbitmap = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize); + m_staticbitmap = new wxStaticBitmap(parent, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize); sizer->Add(m_staticbitmap, 1, wxEXPAND|wxALL, 0); SetSizer(sizer); Layout(); diff --git a/src/slic3r/GUI/SelectMachine.hpp b/src/slic3r/GUI/SelectMachine.hpp index bd00b4a5bb..c3f6ed4988 100644 --- a/src/slic3r/GUI/SelectMachine.hpp +++ b/src/slic3r/GUI/SelectMachine.hpp @@ -230,7 +230,7 @@ private: }; #define SELECT_MACHINE_DIALOG_BUTTON_SIZE wxSize(FromDIP(68), FromDIP(24)) -#define SELECT_MACHINE_DIALOG_SIMBOOK_SIZE wxSize(FromDIP(350), FromDIP(70)) +#define SELECT_MACHINE_DIALOG_SIMBOOK_SIZE wxSize(FromDIP(360), FromDIP(70)) enum PrintDialogStatus { @@ -365,7 +365,8 @@ public: void check_focus(wxWindow* window); void check_fcous_state(wxWindow* window); - wxWindow *create_ams_checkbox(wxString title, wxWindow *parent, wxString tooltip); + void popup_filament_backup(); + wxWindow* create_ams_checkbox(wxString title, wxWindow* parent, wxString tooltip); wxWindow* create_item_checkbox(wxString title, wxWindow* parent, wxString tooltip, std::string param); void update_select_layout(MachineObject *obj); void prepare_mode(); @@ -381,8 +382,8 @@ public: PrintDialogStatus get_status() { return m_print_status; } bool is_same_printer_model(); - - bool Show(bool show); + void sys_color_changed(); + bool Show(bool show); /* model */ wxObjectDataPtr machine_model; @@ -431,7 +432,7 @@ protected: void on_set_finish_mapping(wxCommandEvent &evt); void on_print_job_cancel(wxCommandEvent &evt); void set_default(); - void on_timer(wxTimerEvent &event); + void on_timer(wxTimerEvent& event); void on_selection_changed(wxCommandEvent &event); void Enable_Refresh_Button(bool en); void Enable_Send_Button(bool en); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 753bbf08b4..d82834e2c8 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -2350,7 +2350,7 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co ::glBegin(GL_QUADS); if ((camera_on_top && type == 1) || (!camera_on_top && type == 2)) - ::glColor4f(1.0f, 0.38f, 0.0f, 1.0f); + ::glColor4f(0.0f, 174.0f / 255.0f, 66.0f / 255.0f, 1.0f); else ::glColor4f(0.8f, 0.8f, 0.8f, 0.5f); ::glVertex3f(min_x, min_y, z1); @@ -2361,7 +2361,7 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co ::glBegin(GL_QUADS); if ((camera_on_top && type == 2) || (!camera_on_top && type == 1)) - ::glColor4f(1.0f, 0.38f, 0.0f, 1.0f); + ::glColor4f(0.0f, 174.0f / 255.0f, 66.0f / 255.0f, 1.0f); else ::glColor4f(0.8f, 0.8f, 0.8f, 0.5f); ::glVertex3f(min_x, min_y, z2); diff --git a/src/slic3r/GUI/SendToPrinter.cpp b/src/slic3r/GUI/SendToPrinter.cpp index 84f47088cb..d549b83f2b 100644 --- a/src/slic3r/GUI/SendToPrinter.cpp +++ b/src/slic3r/GUI/SendToPrinter.cpp @@ -363,6 +363,11 @@ SendToPrinterDialog::SendToPrinterDialog(Plater *plater) m_rename_input->SetMinSize(wxSize(FromDIP(380), FromDIP(24))); m_rename_input->SetMaxSize(wxSize(FromDIP(380), FromDIP(24))); m_rename_input->Bind(wxEVT_TEXT_ENTER, [this](auto& e) {on_rename_enter();}); + m_rename_input->Bind(wxEVT_KILL_FOCUS, [this](auto& e) { + if (!m_rename_input->HasFocus() && !m_rename_text->HasFocus()) + on_rename_enter(); + else + e.Skip(); }); rename_edit_sizer_v->Add(m_rename_input, 1, wxALIGN_CENTER, 0); @@ -874,17 +879,6 @@ void SendToPrinterDialog::on_selection_changed(wxCommandEvent &event) return; } - //check ip address - if (obj->is_function_supported(PrinterFunction::FUNC_SEND_TO_SDCARD)) { - if (obj->dev_ip.empty() || obj->get_access_code().empty()) { - BOOST_LOG_TRIVIAL(info) << "MachineObject IP is empty "; - std::string app_config_dev_ip = Slic3r::GUI::wxGetApp().app_config->get("ip_address", obj->dev_id); - if (app_config_dev_ip.empty() || obj->get_access_code().empty()) { - wxGetApp().show_ip_address_enter_dialog(); - } - } - } - update_show_status(); } diff --git a/src/slic3r/GUI/StatusPanel.cpp b/src/slic3r/GUI/StatusPanel.cpp index 37e6081e6a..73a61ebf16 100644 --- a/src/slic3r/GUI/StatusPanel.cpp +++ b/src/slic3r/GUI/StatusPanel.cpp @@ -190,15 +190,16 @@ void StatusBasePanel::init_bitmaps() m_bitmap_extruder_filled_load = *cache.load_png("monitor_extruder_filled_load", FromDIP(28), FromDIP(70), false, false); m_bitmap_extruder_empty_unload = *cache.load_png("monitor_extruder_empty_unload", FromDIP(28), FromDIP(70), false, false); m_bitmap_extruder_filled_unload = *cache.load_png("monitor_extruder_filled_unload", FromDIP(28), FromDIP(70), false, false); - m_bitmap_sdcard_state_abnormal = ScalableBitmap(this, "sdcard_state_abnormal", 20); - m_bitmap_sdcard_state_normal = ScalableBitmap(this, "sdcard_state_normal", 20); - m_bitmap_sdcard_state_no = ScalableBitmap(this, "sdcard_state_no", 20); - m_bitmap_recording_on = ScalableBitmap(this, "monitor_recording_on", 20); - m_bitmap_recording_off = ScalableBitmap(this, "monitor_recording_off", 20); - m_bitmap_timelapse_on = ScalableBitmap(this, "monitor_timelapse_on", 20); - m_bitmap_timelapse_off = ScalableBitmap(this, "monitor_timelapse_off", 20); - m_bitmap_vcamera_on = ScalableBitmap(this, "monitor_vcamera_on", 20); - m_bitmap_vcamera_off = ScalableBitmap(this, "monitor_vcamera_off", 20); + + m_bitmap_sdcard_state_abnormal = ScalableBitmap(this, wxGetApp().dark_mode() ? "sdcard_state_abnormal_dark" : "sdcard_state_abnormal", 20); + m_bitmap_sdcard_state_normal = ScalableBitmap(this, wxGetApp().dark_mode() ? "sdcard_state_normal_dark" : "sdcard_state_normal", 20); + m_bitmap_sdcard_state_no = ScalableBitmap(this, wxGetApp().dark_mode() ? "sdcard_state_no_dark" : "sdcard_state_no", 20); + m_bitmap_recording_on = ScalableBitmap(this, wxGetApp().dark_mode() ? "monitor_recording_on_dark" : "monitor_recording_on", 20); + m_bitmap_recording_off = ScalableBitmap(this, wxGetApp().dark_mode() ? "monitor_recording_off_dark" : "monitor_recording_off", 20); + m_bitmap_timelapse_on = ScalableBitmap(this, wxGetApp().dark_mode() ? "monitor_timelapse_on_dark" : "monitor_timelapse_on", 20); + m_bitmap_timelapse_off = ScalableBitmap(this, wxGetApp().dark_mode() ? "monitor_timelapse_off_dark" : "monitor_timelapse_off", 20); + m_bitmap_vcamera_on = ScalableBitmap(this, wxGetApp().dark_mode() ? "monitor_vcamera_on_dark" : "monitor_vcamera_on", 20); + m_bitmap_vcamera_off = ScalableBitmap(this, wxGetApp().dark_mode() ? "monitor_vcamera_off_dark" : "monitor_vcamera_off", 20); } @@ -508,7 +509,7 @@ wxBoxSizer *StatusBasePanel::create_project_task_page(wxWindow *parent) panel_button_block->SetSize(wxSize(TASK_BUTTON_SIZE.x * 2 + FromDIP(5) * 2, -1)); panel_button_block->SetBackgroundColour(*wxWHITE); - m_staticText_layers = new wxStaticText(penel_text, wxID_ANY, _L("Layers: N/A")); + m_staticText_layers = new wxStaticText(penel_text, wxID_ANY, _L("Layer: N/A")); m_staticText_layers->SetFont(wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("HarmonyOS Sans SC"))); m_staticText_layers->SetForegroundColour(wxColour(146, 146, 146)); m_staticText_layers->Hide(); @@ -1096,11 +1097,11 @@ wxBoxSizer *StatusBasePanel::create_ams_group(wxWindow *parent) return sizer; } -void StatusBasePanel::show_ams_group(bool show, bool support_virtual_tray, bool support_vt_load) +void StatusBasePanel::show_ams_group(bool show, bool support_virtual_tray, bool support_extrustion_cali, bool support_vt_load) { m_ams_control->Show(true); m_ams_control_box->Show(true); - m_ams_control->show_noams_mode(show, support_virtual_tray, support_vt_load); + m_ams_control->show_noams_mode(show, support_virtual_tray, support_extrustion_cali, support_vt_load); if (m_show_ams_group != show) { Fit(); } @@ -1266,6 +1267,7 @@ StatusPanel::StatusPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, co Bind(EVT_AMS_EXTRUSION_CALI, &StatusPanel::on_filament_extrusion_cali, this); Bind(EVT_AMS_LOAD, &StatusPanel::on_ams_load, this); Bind(EVT_AMS_UNLOAD, &StatusPanel::on_ams_unload, this); + Bind(EVT_AMS_FILAMENT_BACKUP, &StatusPanel::on_ams_filament_backup, this); Bind(EVT_AMS_SETTINGS, &StatusPanel::on_ams_setting_click, this); Bind(EVT_AMS_REFRESH_RFID, &StatusPanel::on_ams_refresh_rfid, this); Bind(EVT_AMS_ON_SELECTED, &StatusPanel::on_ams_selected, this); @@ -1591,7 +1593,7 @@ void StatusPanel::show_recenter_dialog() { obj->command_go_home(); } -void StatusPanel::show_error_message(wxString msg, std::string print_error_str) +void StatusPanel::show_error_message(MachineObject* obj, wxString msg, std::string print_error_str) { if (msg.IsEmpty()) { if (m_panel_error_txt->IsShown()) { @@ -1618,6 +1620,14 @@ void StatusPanel::show_error_message(wxString msg, std::string print_error_str) m_print_error_dlg->update_title_style(_L("Warning"), SecondaryCheckDialog::ButtonStyle::ONLY_CONFIRM, this); } m_print_error_dlg->update_text(msg); + + m_print_error_dlg->Bind(EVT_SECONDARY_CHECK_CONFIRM, [this, obj](wxCommandEvent& e) { + if (obj) { + obj->command_clean_print_error(obj->subtask_id_, obj->print_error); + } + }); + + m_print_error_dlg->on_show(); } } @@ -1626,7 +1636,7 @@ void StatusPanel::update_error_message() { if (obj->print_error <= 0) { before_error_code = obj->print_error; - show_error_message(wxEmptyString); + show_error_message(obj, wxEmptyString); return; } else if (before_error_code != obj->print_error && obj->print_error != skip_print_error) { before_error_code = obj->print_error; @@ -1646,7 +1656,7 @@ void StatusPanel::update_error_message() error_msg = wxString::Format("%s[%s %s]", error_msg, print_error_str, show_time); - show_error_message(error_msg, print_error_str); + show_error_message(obj, error_msg, print_error_str); } else { BOOST_LOG_TRIVIAL(info) << "show print error! error_msg is empty, print error = " << obj->print_error; } @@ -1898,6 +1908,8 @@ void StatusPanel::update_ams(MachineObject *obj) } if (m_filament_setting_dlg) { m_filament_setting_dlg->obj = obj; } + bool is_none_ams_mode = false; + if (!obj || !obj->is_connected() || obj->amsList.empty() @@ -1912,19 +1924,30 @@ void StatusPanel::update_ams(MachineObject *obj) BOOST_LOG_TRIVIAL(trace) << "machine object" << obj->dev_name << " was disconnected, set show_ams_group is false"; } bool is_support_extrusion_cali = obj->is_function_supported(PrinterFunction::FUNC_EXTRUSION_CALI); - if (is_support_extrusion_cali) { - m_ams_control->update_vams_kn_value(obj->vt_tray); + bool is_support_virtual_tray = obj->is_function_supported(PrinterFunction::FUNC_VIRTUAL_TYAY); + + if (is_support_virtual_tray) { + m_ams_control->update_vams_kn_value(obj->vt_tray, obj); } - show_ams_group(false, obj->is_function_supported(PrinterFunction::FUNC_EXTRUSION_CALI), obj->is_support_filament_edit_virtual_tray); - return; + show_ams_group(false, obj->is_function_supported(PrinterFunction::FUNC_VIRTUAL_TYAY), obj->is_function_supported(PrinterFunction::FUNC_EXTRUSION_CALI), obj->is_support_filament_edit_virtual_tray); + is_none_ams_mode = true; + //return; } bool is_support_extrusion_cali = obj->is_function_supported(PrinterFunction::FUNC_EXTRUSION_CALI); - if (is_support_extrusion_cali) { - m_ams_control->update_vams_kn_value(obj->vt_tray); + bool is_support_virtual_tray = obj->is_function_supported(PrinterFunction::FUNC_VIRTUAL_TYAY); + bool is_support_filament_backup = obj->is_function_supported(PrinterFunction::FUNC_FILAMENT_BACKUP); + + m_ams_control->show_filament_backup(is_support_filament_backup); + + if (is_support_virtual_tray) { + m_ams_control->update_vams_kn_value(obj->vt_tray, obj); } - show_ams_group(true, obj->is_function_supported(PrinterFunction::FUNC_EXTRUSION_CALI), obj->is_support_filament_edit_virtual_tray); + if (!is_none_ams_mode) { + show_ams_group(true, obj->is_function_supported(PrinterFunction::FUNC_VIRTUAL_TYAY), obj->is_function_supported(PrinterFunction::FUNC_EXTRUSION_CALI), obj->is_support_filament_edit_virtual_tray); + } + if (m_filament_setting_dlg) m_filament_setting_dlg->update(); std::vector ams_info; @@ -1964,26 +1987,27 @@ void StatusPanel::update_ams(MachineObject *obj) is_vt_tray = true; // set segment 1, 2 - if ( obj->m_ams_id != curr_ams_id || obj->m_tray_now == std::to_string(VIRTUAL_TRAY_ID) ) { - m_ams_control->SetAmsStep(curr_ams_id, obj->m_tray_id, AMSPassRoadType::AMS_ROAD_TYPE_UNLOAD, AMSPassRoadSTEP::AMS_ROAD_STEP_NONE); + if (obj->m_tray_now == std::to_string(VIRTUAL_TRAY_ID) ) { + m_ams_control->SetAmsStep(obj->m_ams_id, obj->m_tray_id, AMSPassRoadType::AMS_ROAD_TYPE_UNLOAD, AMSPassRoadSTEP::AMS_ROAD_STEP_NONE); } else { if (obj->m_tray_now != "255" && obj->is_filament_at_extruder() && !obj->m_tray_id.empty()) { - m_ams_control->SetAmsStep(curr_ams_id, obj->m_tray_id, AMSPassRoadType::AMS_ROAD_TYPE_LOAD, AMSPassRoadSTEP::AMS_ROAD_STEP_COMBO_LOAD_STEP2); + m_ams_control->SetAmsStep(obj->m_ams_id, obj->m_tray_id, AMSPassRoadType::AMS_ROAD_TYPE_LOAD, AMSPassRoadSTEP::AMS_ROAD_STEP_COMBO_LOAD_STEP2); } else if (obj->m_tray_now != "255") { - m_ams_control->SetAmsStep(curr_ams_id, obj->m_tray_id, AMSPassRoadType::AMS_ROAD_TYPE_LOAD, AMSPassRoadSTEP::AMS_ROAD_STEP_COMBO_LOAD_STEP1); + m_ams_control->SetAmsStep(obj->m_ams_id, obj->m_tray_id, AMSPassRoadType::AMS_ROAD_TYPE_LOAD, AMSPassRoadSTEP::AMS_ROAD_STEP_COMBO_LOAD_STEP1); } else { - m_ams_control->SetAmsStep(curr_ams_id, obj->m_tray_id, AMSPassRoadType::AMS_ROAD_TYPE_UNLOAD, AMSPassRoadSTEP::AMS_ROAD_STEP_NONE); + m_ams_control->SetAmsStep(obj->m_ams_id, obj->m_tray_id, AMSPassRoadType::AMS_ROAD_TYPE_UNLOAD, AMSPassRoadSTEP::AMS_ROAD_STEP_NONE); } } // set segment 3 if (obj->m_tray_now == std::to_string(VIRTUAL_TRAY_ID)) { - m_ams_control->SetExtruder(obj->is_filament_at_extruder(), obj->vt_tray.get_color()); + m_ams_control->SetExtruder(obj->is_filament_at_extruder(), true, obj->vt_tray.get_color()); } else { - m_ams_control->SetExtruder(obj->is_filament_at_extruder(), m_ams_control->GetCanColour(obj->m_ams_id, obj->m_tray_id)); + m_ams_control->SetExtruder(obj->is_filament_at_extruder(), false, m_ams_control->GetCanColour(obj->m_ams_id, obj->m_tray_id)); + } if (obj->ams_status_main == AMS_STATUS_MAIN_FILAMENT_CHANGE) { @@ -2032,7 +2056,12 @@ void StatusPanel::update_ams(MachineObject *obj) } } else if (obj->ams_status_sub == 0x05) { if (!obj->is_ams_unload()) { - m_ams_control->SetFilamentStep(FilamentStep::STEP_PUSH_NEW_FILAMENT, FilamentStepType::STEP_TYPE_LOAD); + if(m_is_load_with_temp){ + m_ams_control->SetFilamentStep(FilamentStep::STEP_CUT_FILAMENT, FilamentStepType::STEP_TYPE_LOAD); + }else{ + m_ams_control->SetFilamentStep(FilamentStep::STEP_PUSH_NEW_FILAMENT, FilamentStepType::STEP_TYPE_LOAD); + } + } else { m_ams_control->SetFilamentStep(FilamentStep::STEP_PUSH_NEW_FILAMENT, FilamentStepType::STEP_TYPE_UNLOAD); @@ -2046,7 +2075,11 @@ void StatusPanel::update_ams(MachineObject *obj) } } else if (obj->ams_status_sub == 0x07) { if (!obj->is_ams_unload()) { - m_ams_control->SetFilamentStep(FilamentStep::STEP_PURGE_OLD_FILAMENT, FilamentStepType::STEP_TYPE_LOAD); + if (m_is_load_with_temp) { + m_ams_control->SetFilamentStep(FilamentStep::STEP_PULL_CURR_FILAMENT, FilamentStepType::STEP_TYPE_LOAD); + }else{ + m_ams_control->SetFilamentStep(FilamentStep::STEP_PURGE_OLD_FILAMENT, FilamentStepType::STEP_TYPE_LOAD); + } } else { m_ams_control->SetFilamentStep(FilamentStep::STEP_PURGE_OLD_FILAMENT, FilamentStepType::STEP_TYPE_UNLOAD); @@ -2134,23 +2167,23 @@ void StatusPanel::update_ams_control_state(bool is_support_virtual_tray, bool is enable[ACTION_BTN_UNLOAD] = false; } - /*if (obj->ams_status_main == AMS_STATUS_MAIN_FILAMENT_CHANGE) { + if (obj->ams_status_main == AMS_STATUS_MAIN_FILAMENT_CHANGE) { enable[ACTION_BTN_LOAD] = false; enable[ACTION_BTN_UNLOAD] = false; - }*/ + } // select current if (is_curr_tray_selected) { enable[ACTION_BTN_LOAD] = false; } - /*if (!obj->is_filament_at_extruder()) { + if (!obj->is_filament_at_extruder()) { enable[ACTION_BTN_UNLOAD] = false; } - if (obj->m_tray_now == "255") { - enable[ACTION_BTN_UNLOAD] = false; - }*/ +// if (obj->m_tray_now == "255") { +// enable[ACTION_BTN_UNLOAD] = false; +// } m_ams_control->SetActionState(enable); } @@ -2255,9 +2288,13 @@ void StatusPanel::update_subtask(MachineObject *obj) wxString prepare_text; if (obj->is_in_prepare()) prepare_text = wxString::Format(_L("Downloading...")); - else if (obj->print_status == "SLICING") - prepare_text = wxString::Format(_L("Cloud Slicing...")); - else + else if (obj->print_status == "SLICING") { + if (obj->queue_number <= 0) { + prepare_text = wxString::Format(_L("Cloud Slicing...")); + } else { + prepare_text = wxString::Format(_L("In Cloud Slicing Queue, there are %s tasks ahead."), std::to_string(obj->queue_number)); + } + } else prepare_text = wxString::Format(_L("Downloading...")); if (obj->gcode_file_prepare_percent >= 0 && obj->gcode_file_prepare_percent <= 100) @@ -2267,7 +2304,7 @@ void StatusPanel::update_subtask(MachineObject *obj) m_staticText_progress_percent->SetLabelText(NA_STR); m_staticText_progress_percent_icon->SetLabelText(wxEmptyString); m_staticText_progress_left->SetLabel(NA_STR); - m_staticText_layers->SetLabelText(wxString::Format(_L("Layers: %s"), NA_STR)); + m_staticText_layers->SetLabelText(wxString::Format(_L("Layer: %s"), NA_STR)); wxString subtask_text = wxString::Format("%s", GUI::from_u8(obj->subtask_name)); m_staticText_subtask_value->SetLabelText(subtask_text); update_basic_print_data(false); @@ -2296,13 +2333,13 @@ void StatusPanel::update_subtask(MachineObject *obj) m_gauge_progress->SetValue(obj->subtask_->task_progress); m_staticText_progress_percent->SetLabelText(wxString::Format("%d", obj->subtask_->task_progress)); m_staticText_progress_percent_icon->SetLabelText("%"); - m_staticText_layers->SetLabelText(wxString::Format(_L("Layers: %d/%d"), obj->curr_layer, obj->total_layers)); + m_staticText_layers->SetLabelText(wxString::Format(_L("Layer: %d/%d"), obj->curr_layer, obj->total_layers)); } else { m_gauge_progress->SetValue(0); m_staticText_progress_percent->SetLabelText(NA_STR); m_staticText_progress_percent_icon->SetLabelText(wxEmptyString); - m_staticText_layers->SetLabelText(wxString::Format(_L("Layers: %s"), NA_STR)); + m_staticText_layers->SetLabelText(wxString::Format(_L("Layer: %s"), NA_STR)); } } wxString subtask_text = wxString::Format("%s", GUI::from_u8(obj->subtask_name)); @@ -2384,7 +2421,7 @@ void StatusPanel::reset_printing_values() update_basic_print_data(false); m_printing_stage_value->SetLabelText(""); m_staticText_progress_left->SetLabelText(NA_STR); - m_staticText_layers->SetLabelText(wxString::Format(_L("Layers: %s"), NA_STR)); + m_staticText_layers->SetLabelText(wxString::Format(_L("Layer: %s"), NA_STR)); m_staticText_progress_percent->SetLabelText(NA_STR); m_staticText_progress_percent_icon->SetLabelText(wxEmptyString); m_bitmap_thumbnail->SetBitmap(m_thumbnail_placeholder.bmp()); @@ -2445,7 +2482,7 @@ bool StatusPanel::check_axis_z_at_home(MachineObject* obj) } void StatusPanel::on_axis_ctrl_z_up_10(wxCommandEvent &event) -{ +{ if (obj) { obj->command_axis_control("Z", 1.0, -10.0f, 900); if (!check_axis_z_at_home(obj)) @@ -2573,6 +2610,14 @@ void StatusPanel::on_ams_load_curr() std::string curr_ams_id = m_ams_control->GetCurentAms(); std::string curr_can_id = m_ams_control->GetCurrentCan(curr_ams_id); + m_ams_control->UpdateStepCtrl(obj->is_filament_at_extruder()); + if(!obj->is_filament_at_extruder()){ + m_is_load_with_temp = true; + }else{ + m_is_load_with_temp = false; + } + + //virtual tray if (curr_ams_id.compare(std::to_string(VIRTUAL_TRAY_ID)) == 0) { /*if (con_load_dlg == nullptr) { @@ -2580,7 +2625,10 @@ void StatusPanel::on_ams_load_curr() con_load_dlg->Bind(EVT_SECONDARY_CHECK_CONFIRM, [this](wxCommandEvent& e) {*/ int old_temp = -1; int new_temp = -1; - AmsTray* curr_tray = obj->get_curr_tray(); + AmsTray* curr_tray = &obj->vt_tray; + + if (!curr_tray) return; + try { if (!curr_tray->nozzle_temp_max.empty() && !curr_tray->nozzle_temp_min.empty()) old_temp = (atoi(curr_tray->nozzle_temp_min.c_str()) + atoi(curr_tray->nozzle_temp_max.c_str())) / 2; @@ -2638,17 +2686,26 @@ void StatusPanel::on_ams_unload(SimpleEvent &event) if (obj) { obj->command_ams_switch(255); } } +void StatusPanel::on_ams_filament_backup(SimpleEvent& event) +{ + if (obj && obj->filam_bak.size() > 0) { + AmsReplaceMaterialDialog* m_replace_material_popup = new AmsReplaceMaterialDialog(this); + m_replace_material_popup->update_machine_obj(obj); + m_replace_material_popup->ShowModal(); + } +} + void StatusPanel::on_ams_setting_click(SimpleEvent &event) { if (!m_ams_setting_dlg) m_ams_setting_dlg = new AMSSetting((wxWindow *) this, wxID_ANY); if (obj) { m_ams_setting_dlg->update_insert_material_read_mode(obj->ams_insert_flag); m_ams_setting_dlg->update_starting_read_mode(obj->ams_power_on_flag); - std::string ams_id = m_ams_control->GetCurentAms(); - if (ams_id.compare(std::to_string(VIRTUAL_TRAY_ID)) == 0) { - wxString txt = _L("AMS settings are not supported for external spool"); - MessageDialog msg_dlg(nullptr, txt, wxEmptyString, wxICON_WARNING | wxOK); - msg_dlg.ShowModal(); + std::string ams_id = m_ams_control->GetCurentShowAms(); + if (obj->amsList.size() == 0) { + /* wxString txt = _L("AMS settings are not supported for external spool"); + MessageDialog msg_dlg(nullptr, txt, wxEmptyString, wxICON_WARNING | wxOK); + msg_dlg.ShowModal();*/ return; } else { try { @@ -2758,6 +2815,14 @@ void StatusPanel::on_filament_edit(wxCommandEvent &event) n_val = wxString::Format("%.3f", tray_it->second->n); wxColor color = AmsTray::decode_color(tray_it->second->color); m_filament_setting_dlg->set_color(color); + + std::vector cols; + for (auto col : tray_it->second->cols) { + cols.push_back( AmsTray::decode_color(col)); + } + + m_filament_setting_dlg->set_colors(cols); + m_filament_setting_dlg->ams_filament_id = tray_it->second->setting_id; m_filament_setting_dlg->m_is_third = !MachineObject::is_bbl_filament(tray_it->second->tag_uid); if (!m_filament_setting_dlg->m_is_third) { @@ -3006,16 +3071,13 @@ void StatusPanel::on_switch_speed(wxCommandEvent &event) }); popUp->Bind(wxEVT_SHOW, [this, popUp](auto &e) { if (!e.IsShown()) { - /* wxGetApp().CallAfter([this, popUp] { - - });*/ popUp->Destroy(); m_showing_speed_popup = false; speed_dismiss_time = boost::posix_time::microsec_clock::universal_time(); } }); - m_ams_control->Bind(EVT_AMS_SHOW_HUMIDITY_TIPS, [this, popUp](auto& e) { + m_ams_control->Bind(EVT_CLEAR_SPEED_CONTROL, [this, popUp](auto& e) { if (m_showing_speed_popup) { if (popUp && popUp->IsShown()) { popUp->Show(false); @@ -3318,8 +3380,17 @@ void StatusPanel::rescale_camera_icons() { m_setting_button->msw_rescale(); - m_bitmap_vcamera_on.msw_rescale(); - m_bitmap_vcamera_off.msw_rescale(); + + m_bitmap_sdcard_state_abnormal = ScalableBitmap(this, wxGetApp().dark_mode()?"sdcard_state_abnormal_dark":"sdcard_state_abnormal", 20); + m_bitmap_sdcard_state_normal = ScalableBitmap(this, wxGetApp().dark_mode()?"sdcard_state_normal_dark":"sdcard_state_normal", 20); + m_bitmap_sdcard_state_no = ScalableBitmap(this, wxGetApp().dark_mode()?"sdcard_state_no_dark":"sdcard_state_no", 20); + m_bitmap_recording_on = ScalableBitmap(this, wxGetApp().dark_mode()?"monitor_recording_on_dark":"monitor_recording_on", 20); + m_bitmap_recording_off = ScalableBitmap(this, wxGetApp().dark_mode()?"monitor_recording_off_dark":"monitor_recording_off", 20); + m_bitmap_timelapse_on = ScalableBitmap(this, wxGetApp().dark_mode()?"monitor_timelapse_on_dark":"monitor_timelapse_on", 20); + m_bitmap_timelapse_off = ScalableBitmap(this, wxGetApp().dark_mode()?"monitor_timelapse_off_dark":"monitor_timelapse_off", 20); + m_bitmap_vcamera_on = ScalableBitmap(this, wxGetApp().dark_mode()?"monitor_vcamera_on_dark":"monitor_vcamera_on", 20); + m_bitmap_vcamera_off = ScalableBitmap(this, wxGetApp().dark_mode()?"monitor_vcamera_off_dark":"monitor_vcamera_off", 20); + if (m_media_play_ctrl->IsStreaming()) { m_bitmap_vcamera_img->SetBitmap(m_bitmap_vcamera_on.bmp()); } @@ -3328,9 +3399,7 @@ void StatusPanel::rescale_camera_icons() } if (!obj) return; - m_bitmap_sdcard_state_no.msw_rescale(); - m_bitmap_sdcard_state_abnormal.msw_rescale(); - m_bitmap_sdcard_state_normal.msw_rescale(); + if (obj->get_sdcard_state() == MachineObject::SdcardState::NO_SDCARD) { m_bitmap_sdcard_img->SetBitmap(m_bitmap_sdcard_state_no.bmp()); } else if (obj->get_sdcard_state() == MachineObject::SdcardState::HAS_SDCARD_NORMAL) { @@ -3341,16 +3410,12 @@ void StatusPanel::rescale_camera_icons() m_bitmap_sdcard_img->SetBitmap(m_bitmap_sdcard_state_normal.bmp()); } - m_bitmap_recording_on.msw_rescale(); - m_bitmap_recording_off.msw_rescale(); if (obj->is_recording()) { m_bitmap_recording_img->SetBitmap(m_bitmap_recording_on.bmp()); } else { m_bitmap_recording_img->SetBitmap(m_bitmap_recording_off.bmp()); } - m_bitmap_timelapse_on.msw_rescale(); - m_bitmap_timelapse_off.msw_rescale(); if (obj->is_timelapse()) { m_bitmap_timelapse_img->SetBitmap(m_bitmap_timelapse_on.bmp()); } else { @@ -3365,6 +3430,7 @@ void StatusPanel::on_sys_color_changed() m_bitmap_speed_active.msw_rescale(); m_switch_speed->SetImages(m_bitmap_speed, m_bitmap_speed); m_ams_control->msw_rescale(); + if (m_filament_setting_dlg) {m_filament_setting_dlg->msw_rescale();} rescale_camera_icons(); } diff --git a/src/slic3r/GUI/StatusPanel.hpp b/src/slic3r/GUI/StatusPanel.hpp index 422dc94fbe..3bdcaa7f8a 100644 --- a/src/slic3r/GUI/StatusPanel.hpp +++ b/src/slic3r/GUI/StatusPanel.hpp @@ -253,7 +253,7 @@ public: wxBoxSizer *create_ams_group(wxWindow *parent); wxBoxSizer *create_settings_group(wxWindow *parent); - void show_ams_group(bool show = true, bool support_virtual_tray = true, bool support_vt_load = true); + void show_ams_group(bool show = true, bool support_virtual_tray = true, bool support_extrustion_cali = true, bool support_vt_load = true); }; @@ -277,6 +277,7 @@ protected: SecondaryCheckDialog* con_load_dlg = nullptr; SecondaryCheckDialog* ctrl_e_hint_dlg = nullptr; SecondaryCheckDialog* sdcard_hint_dlg = nullptr; + FanControlPopup* m_fan_control_popup{nullptr}; ExtrusionCalibration *m_extrusion_cali_dlg{nullptr}; @@ -289,6 +290,7 @@ protected: int m_last_timelapse = -1; int m_last_extrusion = -1; int m_last_vcamera = -1; + bool m_is_load_with_temp = false; wxWebRequest web_request; bool bed_temp_input = false; @@ -312,7 +314,7 @@ protected: void on_subtask_pause_resume(wxCommandEvent &event); void on_subtask_abort(wxCommandEvent &event); void on_print_error_clean(wxCommandEvent &event); - void show_error_message(wxString msg, std::string print_error_str = ""); + void show_error_message(MachineObject* obj, wxString msg, std::string print_error_str = ""); void error_info_reset(); void show_recenter_dialog(); @@ -340,7 +342,8 @@ protected: void on_ams_load(SimpleEvent &event); void on_ams_load_curr(); void on_ams_unload(SimpleEvent &event); - void on_ams_setting_click(SimpleEvent &event); + void on_ams_filament_backup(SimpleEvent& event); + void on_ams_setting_click(SimpleEvent& event); void on_filament_edit(wxCommandEvent &event); void on_ext_spool_edit(wxCommandEvent &event); void on_filament_extrusion_cali(wxCommandEvent &event); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index b0b6924ec3..e15bfe0d77 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1468,7 +1468,7 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) if (is_support_filament(interface_filament_id) && !(m_config->opt_float("support_top_z_distance") == 0 && m_config->opt_float("support_interface_spacing") == 0 && m_config->opt_enum("support_interface_pattern") == SupportMaterialInterfacePattern::smipConcentric)) { wxString msg_text = _L("When using support material for the support interface, We recommend the following settings:\n" - "0 top z distance, 0 interface spacing, concentric pattern."); + "0 top z distance, 0 interface spacing, concentric pattern and disable independent support layer height"); msg_text += "\n\n" + _L("Change these settings automatically? \n" "Yes - Change these settings automatically\n" "No - Do not change these settings for me"); @@ -1478,6 +1478,7 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) new_conf.set_key_value("support_top_z_distance", new ConfigOptionFloat(0)); new_conf.set_key_value("support_interface_spacing", new ConfigOptionFloat(0)); new_conf.set_key_value("support_interface_pattern", new ConfigOptionEnum(SupportMaterialInterfacePattern::smipConcentric)); + new_conf.set_key_value("independent_support_layer_height", new ConfigOptionBool(false)); m_config_manipulation.apply(m_config, &new_conf); } wxGetApp().plater()->update(); @@ -2018,10 +2019,10 @@ void TabPrint::build() optgroup->append_single_option_line("support_expansion", "support#base-pattern"); //optgroup->append_single_option_line("support_interface_loop_pattern"); - optgroup->append_single_option_line("support_object_xy_distance", "support#supportobject-xy-distance"); + optgroup->append_single_option_line("support_object_xy_distance", "support"); optgroup->append_single_option_line("bridge_no_support", "support#base-pattern"); optgroup->append_single_option_line("max_bridge_length", "support#base-pattern"); - //optgroup->append_single_option_line("independent_support_layer_height"); + optgroup->append_single_option_line("independent_support_layer_height", "support"); page = add_options_page(L("Others"), "advanced"); optgroup = page->new_optgroup(L("Bed adhension"), L"param_adhension"); @@ -2075,6 +2076,13 @@ void TabPrint::build() option.opt.height = 15; optgroup->append_single_option_line(option); + optgroup = page->new_optgroup(L("Post-processing scripts"), L"param_gcode", 0); + option = optgroup->get_option("post_process"); + option.opt.full_width = true; + option.opt.is_code = true; + option.opt.height = 15; + optgroup->append_single_option_line(option); + #if 0 //page = add_options_page(L("Dependencies"), "advanced.png"); // optgroup = page->new_optgroup(L("Profile dependencies")); @@ -2328,8 +2336,9 @@ void TabPrintModel::reset_model_config() wxGetApp().plater()->take_snapshot(std::string("Reset Options")); for (auto config : m_object_configs) { auto rmkeys = intersect(m_keys, config.second->keys()); - for (auto & k : rmkeys) + for (auto& k : rmkeys) { config.second->erase(k); + } notify_changed(config.first); } update_model_config(); @@ -2450,6 +2459,44 @@ void TabPrintPart::notify_changed(ObjectBase * object) wxGetApp().obj_list()->object_config_options_changed({vol->get_object(), vol}); } +static std::string layer_height = "layer_height"; +TabPrintLayer::TabPrintLayer(ParamsPanel* parent) : + TabPrintModel(parent, concat({ layer_height }, PrintRegionConfig().keys())) +{ + m_parent_tab = wxGetApp().get_model_tab(); +} + +void TabPrintLayer::notify_changed(ObjectBase * object) +{ + for (auto config : m_object_configs) { + if (!config.second->has(layer_height)) { + auto option = m_parent_tab->get_config()->option(layer_height); + config.second->set_key_value(layer_height, option->clone()); + } + auto objects_list = wxGetApp().obj_list(); + wxDataViewItemArray items; + objects_list->GetSelections(items); + for (auto item : items) + objects_list->add_settings_item(item, &config.second->get()); + } +} + +void TabPrintLayer::update_custom_dirty() +{ + for (auto k : m_null_keys) m_options_list[k] = 0; + for (auto k : m_all_keys) m_options_list[k] &= ~osSystemValue; + + auto option = m_parent_tab->get_config()->option(layer_height); + for (auto config : m_object_configs) { + if (!config.second->has(layer_height)) { + config.second->set_key_value(layer_height, option->clone()); + m_options_list[layer_height] = osInitValue | osSystemValue; + } + else if (config.second->opt_float(layer_height) == option->getFloat()) + m_options_list[layer_height] = osInitValue | osSystemValue; + } +} + bool Tab::validate_custom_gcode(const wxString& title, const std::string& gcode) { std::vector tags; diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index db78c94d64..d1d2ca0961 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -461,7 +461,7 @@ public: void update_model_config(); - void reset_model_config(); + virtual void reset_model_config(); bool has_key(std::string const &key); @@ -506,6 +506,17 @@ protected: virtual void notify_changed(ObjectBase * object) override; }; +class TabPrintLayer : public TabPrintModel +{ +public: + //BBS: GUI refactor + TabPrintLayer(ParamsPanel* parent); + ~TabPrintLayer() {} +protected: + virtual void notify_changed(ObjectBase* object) override; + virtual void update_custom_dirty() override; +}; + class TabFilament : public Tab { private: diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 07b703d5fd..90a864de16 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -27,6 +27,7 @@ #include "BitmapCache.hpp" #include "PresetComboBoxes.hpp" #include "Widgets/RoundedRectangle.hpp" +#include "Widgets/CheckBox.hpp" using boost::optional; @@ -818,6 +819,22 @@ UnsavedChangesDialog::UnsavedChangesDialog(Preset::Type type, PresetCollection * } +inline int UnsavedChangesDialog::ShowModal() +{ + auto choise_key = "save_preset_choise"; + auto choise = wxGetApp().app_config->get(choise_key); + long result = 0; + if ((m_buttons & REMEMBER_CHOISE) && !choise.empty() && wxString(choise).ToLong(&result) && (1 << result) & (m_buttons | DONT_SAVE)) { + m_exit_action = Action(result); + return 0; + } + int r = wxDialog::ShowModal(); + if (r != wxID_CANCEL && dynamic_cast<::CheckBox*>(FindWindowById(wxID_APPLY))->GetValue()) { + wxGetApp().app_config->set(choise_key, std::to_string(int(m_exit_action))); + } + return r; +} + void UnsavedChangesDialog::build(Preset::Type type, PresetCollection *dependent_presets, const std::string &new_selected_preset, const wxString &header) { SetBackgroundColour(*wxWHITE); @@ -935,6 +952,17 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection *dependent_ wxBoxSizer *m_sizer_button = new wxBoxSizer(wxHORIZONTAL); + auto checkbox_sizer = new wxBoxSizer(wxHORIZONTAL); + auto checkbox = new ::CheckBox(this, wxID_APPLY); + checkbox_sizer->Add(checkbox, 0, wxALL | wxALIGN_CENTER, FromDIP(2)); + + auto checkbox_text = new wxStaticText(this, wxID_ANY, _L("Remember my choice."), wxDefaultPosition, wxDefaultSize, 0); + checkbox_sizer->Add(checkbox_text, 0, wxALL | wxALIGN_CENTER, FromDIP(2)); + checkbox_text->SetFont(::Label::Body_13); + checkbox_text->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#323A3D"))); + m_sizer_button->Add(checkbox_sizer, 0, wxLEFT, FromDIP(22)); + checkbox_sizer->Show(bool(m_buttons & REMEMBER_CHOISE)); + m_sizer_button->Add(0, 0, 1, 0, 0); // Add Buttons @@ -1663,26 +1691,6 @@ void UnsavedChangesDialog::on_sys_color_changed() bool UnsavedChangesDialog::check_option_valid() { - auto itor = std::find_if(m_presetitems.begin(), m_presetitems.end(), [](const PresetItem &item) { - return item.opt_key == "timelapse_type"; - }); - - if (itor != m_presetitems.end()) { - PresetBundle *preset_bundle = wxGetApp().preset_bundle; - Preset * new_preset = preset_bundle->printers.find_preset(m_new_selected_preset_name); - if (new_preset == nullptr) - return false; - - std::string str_print_type = new_preset->get_current_printer_type(preset_bundle); - if (str_print_type == "C11" && itor->new_value.ToStdString() == "Smooth") { - MessageDialog dlg(wxGetApp().plater(), _L("The P1P printer does not support smooth timelapse, use traditional timelapse instead."), - _L("Warning"), wxICON_WARNING | wxOK); - dlg.ShowModal(); - m_presetitems.erase(itor); - return false; - } - } - return true; } diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp index 04dc15d81b..b1bfbd0eb4 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.hpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp @@ -277,9 +277,9 @@ protected: enum class Action { Undef, - Transfer, + Transfer, // Or KEEP + Save, Discard, - Save }; static constexpr char ActTransfer[] = "transfer"; @@ -320,6 +320,7 @@ public: KEEP = 2, SAVE = 4, DONT_SAVE = 8, + REMEMBER_CHOISE = 0x10000 }; // show unsaved changes when preset is switching @@ -328,7 +329,9 @@ public: UnsavedChangesDialog(const wxString& caption, const wxString& header, const std::string& app_config_key, int act_buttons); ~UnsavedChangesDialog(){}; - void build(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset, const wxString& header = ""); + int ShowModal(); + + void build(Preset::Type type, PresetCollection *dependent_presets, const std::string &new_selected_preset, const wxString &header = ""); void update(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset, const wxString& header); void update_list(); std::string subreplace(std::string resource_str, std::string sub_str, std::string new_str); diff --git a/src/slic3r/GUI/UpgradePanel.cpp b/src/slic3r/GUI/UpgradePanel.cpp index 79e5c8de34..547824dc34 100644 --- a/src/slic3r/GUI/UpgradePanel.cpp +++ b/src/slic3r/GUI/UpgradePanel.cpp @@ -854,8 +854,8 @@ UpgradePanel::~UpgradePanel() void UpgradePanel::msw_rescale() { - /*if (m_push_upgrade_panel) - m_push_upgrade_panel->msw_rescale();*/ + if (m_push_upgrade_panel) + m_push_upgrade_panel->msw_rescale(); } void UpgradePanel::clean_push_upgrade_panel() @@ -907,7 +907,7 @@ void UpgradePanel::update(MachineObject *obj) force_dlg->Bind(EVT_SECONDARY_CHECK_CONFIRM, [this](wxCommandEvent& e) { if (m_obj) { m_obj->command_upgrade_confirm(); - m_obj->upgrade_display_state == MachineObject::UpgradingDisplayState::UpgradingInProgress; + m_obj->upgrade_display_state = MachineObject::UpgradingDisplayState::UpgradingInProgress; m_obj->upgrade_display_hold_count = HOLD_COUNT_MAX; } }); diff --git a/src/slic3r/GUI/WebGuideDialog.cpp b/src/slic3r/GUI/WebGuideDialog.cpp index f7bb98c499..35a6522f9f 100644 --- a/src/slic3r/GUI/WebGuideDialog.cpp +++ b/src/slic3r/GUI/WebGuideDialog.cpp @@ -75,11 +75,11 @@ GuideFrame::GuideFrame(GUI_App *pGUI, long style) wxSize pSize = FromDIP(wxSize(820, 660)); SetSize(pSize); - CenterOnParent(); - //int screenheight = wxSystemSettings::GetMetric(wxSYS_SCREEN_Y, NULL); - //int screenwidth = wxSystemSettings::GetMetric(wxSYS_SCREEN_X, NULL); - //int MaxY = (screenheight - pSize.y) > 0 ? (screenheight - pSize.y) / 2 : 0; - //MoveWindow(this->m_hWnd, (screenwidth - pSize.x) / 2, MaxY, pSize.x, pSize.y, TRUE); + int screenheight = wxSystemSettings::GetMetric(wxSYS_SCREEN_Y, NULL); + int screenwidth = wxSystemSettings::GetMetric(wxSYS_SCREEN_X, NULL); + int MaxY = (screenheight - pSize.y) > 0 ? (screenheight - pSize.y) / 2 : 0; + wxPoint tmpPT((screenwidth - pSize.x) / 2, MaxY); + Move(tmpPT); #ifdef __WXMSW__ this->Bind(wxEVT_CHAR_HOOK, [this](wxKeyEvent& e) { if ((m_page == BBL_FILAMENT_ONLY || m_page == BBL_MODELS_ONLY) && e.GetKeyCode() == WXK_ESCAPE) { diff --git a/src/slic3r/GUI/WebUserLoginDialog.cpp b/src/slic3r/GUI/WebUserLoginDialog.cpp index c79966cab2..643d187028 100644 --- a/src/slic3r/GUI/WebUserLoginDialog.cpp +++ b/src/slic3r/GUI/WebUserLoginDialog.cpp @@ -109,7 +109,11 @@ ZUserLogin::ZUserLogin() : wxDialog((wxWindow *) (wxGetApp().mainframe), wxID_AN wxSize pSize = FromDIP(wxSize(650, 840)); SetSize(pSize); - CenterOnParent(); + int screenheight = wxSystemSettings::GetMetric(wxSYS_SCREEN_Y, NULL); + int screenwidth = wxSystemSettings::GetMetric(wxSYS_SCREEN_X, NULL); + int MaxY = (screenheight - pSize.y) > 0 ? (screenheight - pSize.y) / 2 : 0; + wxPoint tmpPT((screenwidth - pSize.x) / 2, MaxY); + Move(tmpPT); //Param m_AutotestToken = ""; diff --git a/src/slic3r/GUI/Widgets/AMSControl.cpp b/src/slic3r/GUI/Widgets/AMSControl.cpp index 01c319d917..6b49b49e39 100644 --- a/src/slic3r/GUI/Widgets/AMSControl.cpp +++ b/src/slic3r/GUI/Widgets/AMSControl.cpp @@ -10,6 +10,7 @@ namespace Slic3r { namespace GUI { static const int LOAD_STEP_COUNT = 5; +static const int LOAD_WITH_TEMP_STEP_COUNT = 3; static const int UNLOAD_STEP_COUNT = 3; static const int VT_LOAD_STEP_COUNT = 4; @@ -19,6 +20,7 @@ wxDEFINE_EVENT(EVT_AMS_EXTRUSION_CALI, wxCommandEvent); wxDEFINE_EVENT(EVT_AMS_LOAD, SimpleEvent); wxDEFINE_EVENT(EVT_AMS_UNLOAD, SimpleEvent); wxDEFINE_EVENT(EVT_AMS_SETTINGS, SimpleEvent); +wxDEFINE_EVENT(EVT_AMS_FILAMENT_BACKUP, SimpleEvent); wxDEFINE_EVENT(EVT_AMS_REFRESH_RFID, wxCommandEvent); wxDEFINE_EVENT(EVT_AMS_ON_SELECTED, wxCommandEvent); wxDEFINE_EVENT(EVT_AMS_ON_FILAMENT_EDIT, wxCommandEvent); @@ -29,6 +31,7 @@ wxDEFINE_EVENT(EVT_AMS_GUIDE_WIKI, wxCommandEvent); wxDEFINE_EVENT(EVT_AMS_RETRY, wxCommandEvent); wxDEFINE_EVENT(EVT_AMS_SHOW_HUMIDITY_TIPS, wxCommandEvent); wxDEFINE_EVENT(EVT_AMS_UNSELETED_VAMS, wxCommandEvent); +wxDEFINE_EVENT(EVT_CLEAR_SPEED_CONTROL, wxCommandEvent); bool AMSinfo::parse_ams_info(Ams *ams, bool remain_flag, bool humidity_flag) { @@ -58,6 +61,10 @@ bool AMSinfo::parse_ams_info(Ams *ams, bool remain_flag, bool humidity_flag) info.material_colour = AMS_TRAY_DEFAULT_COL; } + for (std::string cols:it->second->cols) { + info.material_cols.push_back(AmsTray::decode_color(cols)); + } + if (MachineObject::is_bbl_filament(it->second->tag_uid)) { info.material_state = AMSCanType::AMS_CAN_TYPE_BRAND; } else { @@ -401,12 +408,108 @@ void AMSextruder::create(wxWindow *parent, wxWindowID id, const wxPoint &pos, co m_bitmap_panel->SetSizer(m_bitmap_sizer); m_bitmap_panel->Layout(); + m_sizer_body->Add( 0, 0, 1, wxEXPAND, 0 ); m_sizer_body->Add(m_bitmap_panel, 0, wxALIGN_CENTER, 0); SetSizer(m_sizer_body); + + Bind(wxEVT_PAINT, &AMSextruder::paintEvent, this); Layout(); } +void AMSextruder::OnVamsLoading(bool load, wxColour col) +{ + m_vams_loading = load; + if (load)m_current_colur = col; + Refresh(); +} + +void AMSextruder::OnAmsLoading(bool load, wxColour col /*= AMS_CONTROL_GRAY500*/) +{ + m_ams_loading = load; + if (load)m_current_colur = col; + Refresh(); +} + +void AMSextruder::paintEvent(wxPaintEvent& evt) +{ + wxPaintDC dc(this); + render(dc); +} + +void AMSextruder::render(wxDC& dc) +{ +#ifdef __WXMSW__ + wxSize size = GetSize(); + wxMemoryDC memdc; + wxBitmap bmp(size.x, size.y); + memdc.SelectObject(bmp); + memdc.Blit({ 0, 0 }, size, &dc, { 0, 0 }); + + { + wxGCDC dc2(memdc); + doRender(dc2); + } + + memdc.SelectObject(wxNullBitmap); + dc.DrawBitmap(bmp, 0, 0); +#else + doRender(dc); +#endif + +} + +void AMSextruder::doRender(wxDC& dc) +{ + //m_current_colur = + wxSize size = GetSize(); + dc.SetPen(wxPen(AMS_CONTROL_GRAY500, 2, wxSOLID)); + dc.SetBrush(wxBrush(*wxTRANSPARENT_BRUSH)); + + if (!m_none_ams_mode) { + dc.DrawLine(size.x / 2, -1, size.x / 2, size.y * 0.6 - 1); + } + + if (m_has_vams) { + dc.DrawRoundedRectangle(-size.x / 2, size.y * 0.1, size.x, size.y, 4); + + if (m_vams_loading) { + dc.SetPen(wxPen(m_current_colur, 6, wxSOLID)); + dc.DrawRoundedRectangle(-size.x / 2, size.y * 0.1, size.x, size.y, 4); + + if (m_current_colur == *wxWHITE && !wxGetApp().dark_mode()) { + dc.SetPen(wxPen(AMS_CONTROL_DEF_BLOCK_BK_COLOUR, 1, wxSOLID)); + dc.DrawRoundedRectangle(-size.x / 2 - FromDIP(3), size.y * 0.1 + FromDIP(3), size.x, size.y, 3); + dc.DrawRoundedRectangle(-size.x / 2 + FromDIP(3), size.y * 0.1 - FromDIP(3), size.x, size.y, 5); + } + } + + if (m_ams_loading && !m_none_ams_mode) { + dc.SetPen(wxPen(m_current_colur, 6, wxSOLID)); + dc.DrawLine(size.x / 2, -1, size.x / 2, size.y * 0.6 - 1); + + if (m_current_colur == *wxWHITE && !wxGetApp().dark_mode()) { + dc.SetPen(wxPen(AMS_CONTROL_DEF_BLOCK_BK_COLOUR, 1, wxSOLID)); + dc.DrawLine(size.x / 2 - FromDIP(4), -1, size.x / 2 - FromDIP(3), size.y * 0.6 - 1); + dc.DrawLine(size.x / 2 + FromDIP(3), -1, size.x / 2 + FromDIP(3), size.y * 0.6 - 1); + } + } + } + else { + if (m_ams_loading) { + dc.SetPen(wxPen(m_current_colur, 6, wxSOLID)); + dc.DrawLine(size.x / 2, -1, size.x / 2, size.y * 0.6 - 1); + + if (m_current_colur == *wxWHITE && !wxGetApp().dark_mode()) { + dc.SetPen(wxPen(AMS_CONTROL_DEF_BLOCK_BK_COLOUR, 1, wxSOLID)); + dc.DrawLine(size.x / 2 - FromDIP(4), -1, size.x / 2 - FromDIP(3), size.y * 0.6 - 1); + dc.DrawLine(size.x / 2 + FromDIP(3), -1, size.x / 2 + FromDIP(3), size.y * 0.6 - 1); + } + } + } + +} + void AMSextruder::msw_rescale() { m_amsSextruder->msw_rescale(); @@ -415,6 +518,87 @@ void AMSextruder::msw_rescale() Refresh(); } +/************************************************* +Description:AMSVirtualRoad +**************************************************/ + +AMSVirtualRoad::AMSVirtualRoad(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size) { create(parent, id, pos, size); } + +AMSVirtualRoad::~AMSVirtualRoad() {} + +void AMSVirtualRoad::OnVamsLoading(bool load, wxColour col) +{ + m_vams_loading = load; + if (load)m_current_color = col; + Refresh(); +} + +void AMSVirtualRoad::create(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size) +{ + wxWindow::Create(parent, id, pos, wxDefaultSize, wxBORDER_NONE); + SetBackgroundColour(AMS_CONTROL_WHITE_COLOUR); + Layout(); + Bind(wxEVT_PAINT, &AMSVirtualRoad::paintEvent, this); +} + +void AMSVirtualRoad::paintEvent(wxPaintEvent& evt) +{ + wxPaintDC dc(this); + render(dc); +} + +void AMSVirtualRoad::render(wxDC& dc) +{ +#ifdef __WXMSW__ + wxSize size = GetSize(); + wxMemoryDC memdc; + wxBitmap bmp(size.x, size.y); + memdc.SelectObject(bmp); + memdc.Blit({ 0, 0 }, size, &dc, { 0, 0 }); + + { + wxGCDC dc2(memdc); + doRender(dc2); + } + + memdc.SelectObject(wxNullBitmap); + dc.DrawBitmap(bmp, 0, 0); +#else + doRender(dc); +#endif +} + +void AMSVirtualRoad::doRender(wxDC& dc) +{ + if (!m_has_vams) return; + + wxSize size = GetSize(); + if (m_vams_loading) { + dc.SetPen(wxPen(m_current_color, 6, wxSOLID)); + } + else { + dc.SetPen(wxPen(AMS_CONTROL_GRAY500, 2, wxSOLID)); + } + + dc.SetBrush(wxBrush(*wxTRANSPARENT_BRUSH)); + dc.DrawRoundedRectangle(size.x / 2, -size.y / 1.1 + FromDIP(1), size.x, size.y, 4); + + if (m_current_color == *wxWHITE && !wxGetApp().dark_mode()) { + dc.SetPen(wxPen(AMS_CONTROL_DEF_BLOCK_BK_COLOUR, 1, wxSOLID)); + dc.DrawRoundedRectangle(size.x / 2 - FromDIP(3), -size.y / 1.1 + FromDIP(4), size.x, size.y, 5); + dc.DrawRoundedRectangle(size.x / 2 + FromDIP(3), -size.y / 1.1 - FromDIP(2), size.x, size.y, 3); + } +} + + +void AMSVirtualRoad::msw_rescale() +{ + Layout(); + Update(); + Refresh(); +} + + /************************************************* Description:AMSLib **************************************************/ @@ -477,10 +661,10 @@ void AMSLib::on_left_down(wxMouseEvent &evt) auto pos = evt.GetPosition(); if (m_info.material_state == AMSCanType::AMS_CAN_TYPE_THIRDBRAND || m_info.material_state == AMSCanType::AMS_CAN_TYPE_BRAND || m_info.material_state == AMSCanType::AMS_CAN_TYPE_VIRTUAL) { - auto left = FromDIP(20); - auto top = (size.y - FromDIP(10) - m_bitmap_editable_light.GetBmpSize().y); - auto right = size.x - FromDIP(20); - auto bottom = size.y - FromDIP(10); + auto left = FromDIP(10); + auto top = (size.y - FromDIP(15) - m_bitmap_editable_light.GetBmpSize().y); + auto right = size.x - FromDIP(10);; + auto bottom = size.y - FromDIP(15); if (pos.x >= left && pos.x <= right && pos.y >= top && top <= bottom) { if (m_selected) { @@ -550,6 +734,7 @@ void AMSLib::render(wxDC &dc) if (m_info.material_state == AMSCanType::AMS_CAN_TYPE_THIRDBRAND || m_info.material_state == AMSCanType::AMS_CAN_TYPE_BRAND || m_info.material_state == AMSCanType::AMS_CAN_TYPE_VIRTUAL) { + if (m_info.material_name.empty() && m_info.material_state != AMSCanType::AMS_CAN_TYPE_VIRTUAL) { auto tsize = dc.GetMultiLineTextExtent("?"); auto pot = wxPoint(0, 0); @@ -561,12 +746,23 @@ void AMSLib::render(wxDC &dc) dc.DrawText(L("?"), pot); } else { auto tsize = dc.GetMultiLineTextExtent(m_info.material_name); + std::vector split_char_arr = {" ", "-"}; + bool has_split = false; + std::string has_split_char = " "; - if (m_info.material_name.find(' ') != std::string::npos) { + for (std::string split_char : split_char_arr) { + if (m_info.material_name.find(split_char) != std::string::npos) { + has_split = true; + has_split_char = split_char; + } + } + + + if (has_split) { dc.SetFont(::Label::Body_12); - auto line_top = m_info.material_name.substr(0, m_info.material_name.find(' ')); - auto line_bottom = m_info.material_name.substr(m_info.material_name.find(' ')); + auto line_top = m_info.material_name.substr(0, m_info.material_name.find(has_split_char)); + auto line_bottom = m_info.material_name.substr(m_info.material_name.find(has_split_char)); auto line_top_tsize = dc.GetMultiLineTextExtent(line_top); auto line_bottom_tsize = dc.GetMultiLineTextExtent(line_bottom); @@ -579,7 +775,7 @@ void AMSLib::render(wxDC &dc) } else { auto pot = wxPoint(0, 0); - if (m_show_kn) { + if (m_obj && m_obj->is_function_supported(PrinterFunction::FUNC_EXTRUSION_CALI)) { pot = wxPoint((libsize.x - tsize.x) / 2, (libsize.y - tsize.y) / 2 - FromDIP(9)); } else { pot = wxPoint((libsize.x - tsize.x) / 2, (libsize.y - tsize.y) / 2 + FromDIP(3)); @@ -589,16 +785,15 @@ void AMSLib::render(wxDC &dc) } //draw k&n - if (m_show_kn) { - wxString str_k = wxString::Format("K %1.3f", m_info.k); - wxString str_n = wxString::Format("N %1.3f", m_info.n); - dc.SetFont(::Label::Body_11); - auto tsize = dc.GetMultiLineTextExtent(str_k); - auto pot_k = wxPoint((libsize.x - tsize.x) / 2, (libsize.y - tsize.y) / 2 - FromDIP(9) + tsize.y); - dc.DrawText(str_k, pot_k); - - //auto pot_n = wxPoint((libsize.x - tsize.x) / 2, (libsize.y - tsize.y) / 2 - FromDIP(18) + tsize.y * 2); - //dc.DrawText(str_n, pot_n); + if (m_obj && m_obj->is_function_supported(PrinterFunction::FUNC_EXTRUSION_CALI)) { + if (m_show_kn){ + wxString str_k = wxString::Format("K %1.3f", m_info.k); + wxString str_n = wxString::Format("N %1.3f", m_info.n); + dc.SetFont(::Label::Body_11); + auto tsize = dc.GetMultiLineTextExtent(str_k); + auto pot_k = wxPoint((libsize.x - tsize.x) / 2, (libsize.y - tsize.y) / 2 - FromDIP(9) + tsize.y); + dc.DrawText(str_k, pot_k); + } } } @@ -689,18 +884,34 @@ void AMSLib::doRender(wxDC &dc) int height = size.y - FromDIP(8); int curr_height = height * float(m_info.material_remain * 1.0 / 100.0); - - int top = height - curr_height; if (curr_height >= FromDIP(6)) { -#ifdef __APPLE__ - dc.DrawRoundedRectangle(FromDIP(4), FromDIP(4) + top, size.x - FromDIP(8), curr_height, m_radius); -#else - dc.DrawRoundedRectangle(FromDIP(4), FromDIP(4) + top, size.x - FromDIP(8), curr_height, m_radius - 1); -#endif - + //gradient + if (m_info.material_cols.size() > 1) { + int left = FromDIP(4); + float total_width = size.x - FromDIP(8); + int gwidth = std::round(total_width / (m_info.material_cols.size() - 1)); + + for (int i = 0; i < m_info.material_cols.size() - 1; i++) { + + if ((left + gwidth) > (size.x - FromDIP(8))) { + gwidth = (size.x - FromDIP(4)) - left; + } + + auto rect = wxRect(left, height - curr_height + FromDIP(4), gwidth, curr_height); + dc.GradientFillLinear(rect, m_info.material_cols[i], m_info.material_cols[i + 1], wxEAST); + left += gwidth; + } + } + else { +#ifdef __APPLE__ + dc.DrawRoundedRectangle(FromDIP(4), FromDIP(4) + top, size.x - FromDIP(8), curr_height, m_radius); +#else + dc.DrawRoundedRectangle(FromDIP(4), FromDIP(4) + top, size.x - FromDIP(8), curr_height, m_radius - 1); +#endif + } } if (top > 2) { @@ -753,6 +964,12 @@ void AMSLib::doRender(wxDC &dc) void AMSLib::Update(Caninfo info, bool refresh) { + DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager(); + if (!dev) return; + if (dev->get_selected_machine() && dev->get_selected_machine() != m_obj) { + m_obj = dev->get_selected_machine(); + } + m_info = info; Layout(); if (refresh) Refresh(); @@ -805,30 +1022,39 @@ AMSRoad::AMSRoad(wxWindow *parent, wxWindowID id, Caninfo info, int canindex, in m_rode_mode = AMSRoadMode::AMS_ROAD_MODE_LEFT_RIGHT; } else if (m_canindex == (maxcan - 1)) { m_rode_mode = AMSRoadMode::AMS_ROAD_MODE_LEFT; + } else if (m_canindex == -1 && maxcan == -1) { + m_rode_mode = AMSRoadMode::AMS_ROAD_MODE_VIRTUAL_TRAY; } else { m_rode_mode = AMSRoadMode::AMS_ROAD_MODE_NONE_ANY_ROAD; } - ams_humidity_0 = ScalableBitmap(this, "ams_humidity_0", 18); - ams_humidity_1 = ScalableBitmap(this, "ams_humidity_1", 18); - ams_humidity_2 = ScalableBitmap(this, "ams_humidity_2", 18); - ams_humidity_3 = ScalableBitmap(this, "ams_humidity_3", 18); - ams_humidity_4 = ScalableBitmap(this, "ams_humidity_4", 18); + ams_humidity_0 = ScalableBitmap(this, "ams_humidity_0", 20); + ams_humidity_1 = ScalableBitmap(this, "ams_humidity_1", 20); + ams_humidity_2 = ScalableBitmap(this, "ams_humidity_2", 20); + ams_humidity_3 = ScalableBitmap(this, "ams_humidity_3", 20); + ams_humidity_4 = ScalableBitmap(this, "ams_humidity_4", 20); create(parent, id, pos, size); Bind(wxEVT_PAINT, &AMSRoad::paintEvent, this); wxWindow::SetBackgroundColour(AMS_CONTROL_DEF_BLOCK_BK_COLOUR); - Bind(wxEVT_MOTION, [this](wxMouseEvent& e) { + Bind(wxEVT_LEFT_UP, [this](wxMouseEvent& e) { if (m_canindex == 3 && m_show_humidity) { auto mouse_pos = ClientToScreen(e.GetPosition()); auto rect = ClientToScreen(wxPoint(0, 0)); if (mouse_pos.x > rect.x + GetSize().x - FromDIP(25) && mouse_pos.y > rect.y + GetSize().y - FromDIP(25)) { - wxCommandEvent event(EVT_AMS_SHOW_HUMIDITY_TIPS); - wxPostEvent(GetParent()->GetParent(), event); + wxCommandEvent show_event(EVT_AMS_SHOW_HUMIDITY_TIPS); + wxPostEvent(GetParent()->GetParent(), show_event); + +#ifdef __WXMSW__ + wxCommandEvent close_event(EVT_CLEAR_SPEED_CONTROL); + wxPostEvent(GetParent()->GetParent(), close_event); +#endif // __WXMSW__ + + } } }); @@ -854,6 +1080,13 @@ void AMSRoad::Update(AMSinfo amsinfo, Caninfo info, int canindex, int maxcan) Refresh(); } +void AMSRoad::OnVamsLoading(bool load, wxColour col /*= AMS_CONTROL_GRAY500*/) +{ + m_vams_loading = load; + if(load)m_road_color = col; + Refresh(); +} + void AMSRoad::SetPassRoadColour(wxColour col) { m_road_color = col; } void AMSRoad::SetMode(AMSRoadMode mode) @@ -927,6 +1160,12 @@ void AMSRoad::doRender(wxDC &dc) // dc.DrawLine(size.x / 2, size.y * 0.6 - 1, size.x, size.y * 0.6 - 1); } + //virtual road + if (m_rode_mode == AMSRoadMode::AMS_ROAD_MODE_VIRTUAL_TRAY) { + dc.SetBrush(wxBrush(m_road_def_color)); + dc.DrawLine(size.x / 2, -1, size.x / 2, size.y - 1); + } + // mode none // if (m_pass_rode_mode.size() == 1 && m_pass_rode_mode[0] == AMSPassRoadMode::AMS_ROAD_MODE_NONE) return; @@ -950,6 +1189,10 @@ void AMSRoad::doRender(wxDC &dc) } } + if (m_rode_mode == AMSRoadMode::AMS_ROAD_MODE_VIRTUAL_TRAY && m_vams_loading) { + dc.DrawLine(size.x / 2, -1, size.x / 2, size.y - 1); + } + // end mode if (m_rode_mode == AMSRoadMode::AMS_ROAD_MODE_END || m_rode_mode == AMSRoadMode::AMS_ROAD_MODE_END_ONLY) { dc.SetPen(wxPen(m_road_def_color, 2, wxSOLID)); @@ -1036,8 +1279,6 @@ void AMSRoad::OnPassRoad(std::vector prord_list) } } } - - //Refresh(); } /************************************************* @@ -1173,7 +1414,31 @@ void AMSItem::doRender(wxDC &dc) dc.SetBrush(AMS_CONTROL_DISABLE_COLOUR); } - dc.DrawRoundedRectangle(left, (size.y - AMS_ITEM_CUBE_SIZE.y) / 2, AMS_ITEM_CUBE_SIZE.x, AMS_ITEM_CUBE_SIZE.y, 2); + if (iter->material_cols.size() > 1) { + int fleft = left; + float total_width = AMS_ITEM_CUBE_SIZE.x; + int gwidth = std::round(total_width / (iter->material_cols.size() - 1)); + + for (int i = 0; i < iter->material_cols.size() - 1; i++) { + + if ((fleft + gwidth) > (AMS_ITEM_CUBE_SIZE.x)) { + gwidth = (fleft + AMS_ITEM_CUBE_SIZE.x) - fleft; + } + + auto rect = wxRect(fleft, (size.y - AMS_ITEM_CUBE_SIZE.y) / 2, gwidth, AMS_ITEM_CUBE_SIZE.y); + dc.GradientFillLinear(rect, iter->material_cols[i], iter->material_cols[i + 1], wxEAST); + fleft += gwidth; + } + + dc.SetPen(wxPen(StateColor::darkModeColorFor(m_background_colour))); + dc.SetBrush(*wxTRANSPARENT_BRUSH); + dc.DrawRoundedRectangle(left - 1, (size.y - AMS_ITEM_CUBE_SIZE.y) / 2 - 1, AMS_ITEM_CUBE_SIZE.x + 2, AMS_ITEM_CUBE_SIZE.y + 2, 2); + + }else { + dc.DrawRoundedRectangle(left, (size.y - AMS_ITEM_CUBE_SIZE.y) / 2, AMS_ITEM_CUBE_SIZE.x, AMS_ITEM_CUBE_SIZE.y, 2); + } + + left += AMS_ITEM_CUBE_SIZE.x; left += m_space; } @@ -1367,6 +1632,16 @@ void AmsCans::SelectCan(std::string canid) } } +wxColour AmsCans::GetTagColr(wxString canid) +{ + auto tag_colour = *wxWHITE; + for (auto i = 0; i < m_can_lib_list.GetCount(); i++) { + CanLibs* lib = m_can_lib_list[i]; + if (canid == lib->canLib->m_info.can_id) tag_colour = lib->canLib->GetLibColour(); + } + return tag_colour; +} + void AmsCans::SetAmsStep(wxString canid, AMSPassRoadType type, AMSPassRoadSTEP step) { @@ -1505,6 +1780,7 @@ AMSControl::AMSControl(wxWindow *parent, wxWindowID id, const wxPoint &pos, cons wxBoxSizer *m_sizer_body = new wxBoxSizer(wxVERTICAL); m_amswin = new wxWindow(this, wxID_ANY, wxDefaultPosition, wxSize(-1, AMS_CAN_ITEM_HEIGHT_SIZE)); m_amswin->SetBackgroundColour(*wxWHITE); + // top - ams tag m_simplebook_amsitems = new wxSimplebook(m_amswin, wxID_ANY); m_simplebook_amsitems->SetSize(wxSize(-1, AMS_CAN_ITEM_HEIGHT_SIZE)); @@ -1533,14 +1809,16 @@ AMSControl::AMSControl(wxWindow *parent, wxWindowID id, const wxPoint &pos, cons wxBoxSizer *m_sizer_bottom = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer *m_sizer_left = new wxBoxSizer(wxVERTICAL); + //ams tip m_sizer_ams_tips = new wxBoxSizer(wxHORIZONTAL); - auto m_ams_tip = new wxStaticText(m_amswin, wxID_ANY, _L("AMS")); + auto m_ams_tip = new Label(m_amswin, _L("AMS")); m_ams_tip->SetFont(::Label::Body_12); m_ams_tip->SetBackgroundColour(*wxWHITE); auto img_amsmapping_tip = new wxStaticBitmap(m_amswin, wxID_ANY, create_scaled_bitmap("enable_ams", this, 16), wxDefaultPosition, wxSize(FromDIP(16), FromDIP(16)), 0); img_amsmapping_tip->SetBackgroundColour(*wxWHITE); - m_sizer_ams_tips->Add(m_ams_tip, 0, wxALIGN_CENTER, 0); - m_sizer_ams_tips->Add(img_amsmapping_tip, 0, wxALL, FromDIP(2)); + + m_sizer_ams_tips->Add(m_ams_tip, 0, wxTOP, FromDIP(5)); + m_sizer_ams_tips->Add(img_amsmapping_tip, 0, wxALL, FromDIP(3)); img_amsmapping_tip->Bind(wxEVT_ENTER_WINDOW, [this, img_amsmapping_tip](auto& e) { wxPoint img_pos = img_amsmapping_tip->ClientToScreen(wxPoint(0, 0)); @@ -1548,13 +1826,45 @@ AMSControl::AMSControl(wxWindow *parent, wxWindowID id, const wxPoint &pos, cons m_ams_introduce_popup.set_mode(true); m_ams_introduce_popup.Position(popup_pos, wxSize(0, 0)); m_ams_introduce_popup.Popup(); - }); - img_amsmapping_tip->Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& e) { +#ifdef __WXMSW__ + wxCommandEvent close_event(EVT_CLEAR_SPEED_CONTROL); + wxPostEvent(this, close_event); +#endif // __WXMSW__ + }); + img_amsmapping_tip->Bind(wxEVT_LEAVE_WINDOW, [this](auto& e) { m_ams_introduce_popup.Dismiss(); }); + //backup tips + +#ifdef FILAMENT_BACKUP + auto m_ams_backup_tip = new Label(m_amswin, _L("Ams filament backup")); + m_ams_backup_tip->SetFont(::Label::Head_12); + m_ams_backup_tip->SetForegroundColour(wxColour(0x00AE42)); + m_ams_backup_tip->SetBackgroundColour(*wxWHITE); + auto m_img_ams_backup = new wxStaticBitmap(m_amswin, wxID_ANY, create_scaled_bitmap("automatic_material_renewal", this, 16), wxDefaultPosition, wxSize(FromDIP(16), FromDIP(16)), 0); + m_img_ams_backup->SetBackgroundColour(*wxWHITE); + + m_sizer_ams_tips->Add(0, 0, 1, wxEXPAND, 0); + m_sizer_ams_tips->Add(m_img_ams_backup, 0, wxALL, FromDIP(3)); + m_sizer_ams_tips->Add(m_ams_backup_tip, 0, wxTOP, FromDIP(5)); + + m_ams_backup_tip->Bind(wxEVT_ENTER_WINDOW, [this, img_amsmapping_tip](auto& e) {SetCursor(wxCURSOR_HAND); }); + m_img_ams_backup->Bind(wxEVT_ENTER_WINDOW, [this, img_amsmapping_tip](auto& e) {SetCursor(wxCURSOR_HAND); }); + + m_ams_backup_tip->Bind(wxEVT_LEAVE_WINDOW, [this](auto& e) {SetCursor(wxCURSOR_ARROW); }); + m_img_ams_backup->Bind(wxEVT_LEAVE_WINDOW, [this](auto& e) {SetCursor(wxCURSOR_ARROW); }); + + m_ams_backup_tip->Bind(wxEVT_LEFT_DOWN, [this](auto& e) {post_event(SimpleEvent(EVT_AMS_FILAMENT_BACKUP)); }); + m_img_ams_backup->Bind(wxEVT_LEFT_DOWN, [this](auto& e) {post_event(SimpleEvent(EVT_AMS_FILAMENT_BACKUP)); }); +#endif // FILAMENT_BACKUP + + + + + //ams cans m_panel_can = new StaticBox(m_amswin, wxID_ANY, wxDefaultPosition, AMS_CANS_SIZE, wxBORDER_NONE); m_panel_can->SetMinSize(AMS_CANS_SIZE); m_panel_can->SetCornerRadius(FromDIP(10)); @@ -1589,20 +1899,6 @@ AMSControl::AMSControl(wxWindow *parent, wxWindowID id, const wxPoint &pos, cons m_none_ams_panel->SetSizer(sizer_ams_panel_h); m_none_ams_panel->Layout(); - /*wxBoxSizer *sizer_ams_panel = new wxBoxSizer(wxHORIZONTAL); - AMSinfo none_ams = AMSinfo{ "0", std::vector{Caninfo{"0", wxEmptyString, *wxWHITE, AMSCanType::AMS_CAN_TYPE_EMPTY}} }; - auto amscans = new AmsCans(m_none_ams_panel, wxID_ANY, none_ams); - sizer_ams_panel->Add(amscans, 0, wxALL, 0); - sizer_ams_panel->Add(0, 0, 0, wxLEFT, 20); - auto m_tip_none_ams = new wxStaticText(m_none_ams_panel, wxID_ANY, _L("Click the pencil icon to edit the filament."), wxDefaultPosition, wxDefaultSize, 0); - m_tip_none_ams->Wrap(150); - m_tip_none_ams->SetFont(::Label::Body_13); - m_tip_none_ams->SetForegroundColour(AMS_CONTROL_GRAY500); - m_tip_none_ams->SetMinSize({150, -1}); - sizer_ams_panel->Add(m_tip_none_ams, 0, wxALIGN_CENTER, 0); - m_none_ams_panel->SetSizer(sizer_ams_panel); - m_none_ams_panel->Layout();*/ - m_simplebook_ams->AddPage(m_simplebook_cans, wxEmptyString, true); m_simplebook_ams->AddPage(m_none_ams_panel, wxEmptyString, false); @@ -1610,7 +1906,7 @@ AMSControl::AMSControl(wxWindow *parent, wxWindowID id, const wxPoint &pos, cons m_panel_can->Layout(); m_sizer_cans->Fit(m_panel_can); - m_sizer_left->Add(m_sizer_ams_tips, 0, wxALIGN_CENTER, 0); + m_sizer_left->Add(m_sizer_ams_tips, 0, wxEXPAND, 0); m_sizer_left->Add(m_panel_can, 1, wxEXPAND, 0); wxBoxSizer *m_sizer_left_bottom = new wxBoxSizer(wxHORIZONTAL); @@ -1624,24 +1920,38 @@ AMSControl::AMSControl(wxWindow *parent, wxWindowID id, const wxPoint &pos, cons m_extruder = new AMSextruder(extruder_pane, wxID_ANY, wxDefaultPosition, AMS_EXTRUDER_SIZE); sizer_sextruder->Add(m_extruder, 0, wxALIGN_CENTER, 0); - m_sizer_left_bottom->Add(extruder_pane, 0, wxLEFT, FromDIP(10)); + m_sizer_left_bottom->Add(extruder_pane, 0, wxALL,0); //m_sizer_left_bottom->Add(0, 0, 0, wxEXPAND, 0); - StateColor btn_bg_green(std::pair(AMS_CONTROL_DISABLE_COLOUR, StateColor::Disabled),std::pair(wxColour(0, 137, 123), StateColor::Pressed), std::pair(wxColour(38, 166, 154), StateColor::Hovered), + StateColor btn_bg_green(std::pair(AMS_CONTROL_DISABLE_COLOUR, StateColor::Disabled), + std::pair(wxColour(0, 137, 123), StateColor::Pressed), + std::pair(wxColour(38, 166, 154), StateColor::Hovered), + std::pair(AMS_CONTROL_BRAND_COLOUR, StateColor::Normal)); - StateColor btn_bg_white(std::pair(AMS_CONTROL_DISABLE_COLOUR, StateColor::Disabled), std::pair(AMS_CONTROL_DISABLE_COLOUR, StateColor::Pressed), - std::pair(AMS_CONTROL_DEF_BLOCK_BK_COLOUR, StateColor::Hovered), - std::pair(AMS_CONTROL_WHITE_COLOUR, StateColor::Normal)); + StateColor btn_bg_white(std::pair(AMS_CONTROL_DISABLE_COLOUR, StateColor::Disabled), + std::pair(AMS_CONTROL_DISABLE_COLOUR, StateColor::Pressed), + std::pair(AMS_CONTROL_DEF_BLOCK_BK_COLOUR, StateColor::Hovered), + std::pair(AMS_CONTROL_WHITE_COLOUR, StateColor::Normal)); - StateColor btn_bd_green(std::pair(AMS_CONTROL_WHITE_COLOUR, StateColor::Disabled), std::pair(AMS_CONTROL_BRAND_COLOUR, StateColor::Enabled)); - StateColor btn_bd_white(std::pair(AMS_CONTROL_WHITE_COLOUR, StateColor::Disabled), std::pair(wxColour(38, 46, 48), StateColor::Enabled)); - StateColor btn_text_green(std::pair(*wxBLACK, StateColor::Disabled), std::pair(AMS_CONTROL_WHITE_COLOUR, StateColor::Enabled)); - //m_sizer_left_bottom->AddStretchSpacer(); + StateColor btn_bd_green(std::pair(wxColour(255,255,254), StateColor::Disabled), + std::pair(AMS_CONTROL_BRAND_COLOUR, StateColor::Enabled)); + + StateColor btn_bd_white(std::pair(wxColour(255,255,254), StateColor::Disabled), + std::pair(wxColour(38, 46, 48), StateColor::Enabled)); + + StateColor btn_text_green(std::pair(wxColour(255,255,254), StateColor::Disabled), + std::pair(wxColour(255,255,254), StateColor::Enabled)); + + StateColor btn_text_white(std::pair(wxColour(255, 255, 254), StateColor::Disabled), + std::pair(wxColour(38, 46, 48), StateColor::Enabled)); m_button_area = new wxWindow(m_amswin, wxID_ANY); m_button_area->SetBackgroundColour(m_amswin->GetBackgroundColour()); + + + wxBoxSizer *m_sizer_button = new wxBoxSizer(wxVERTICAL); wxBoxSizer *m_sizer_button_area = new wxBoxSizer(wxHORIZONTAL); m_button_extrusion_cali = new Button(m_button_area, _L("Cali")); @@ -1654,12 +1964,13 @@ AMSControl::AMSControl(wxWindow *parent, wxWindowID id, const wxPoint &pos, cons m_button_extruder_feed = new Button(m_button_area, _L("Load Filament")); m_button_extruder_feed->SetBackgroundColor(btn_bg_green); m_button_extruder_feed->SetBorderColor(btn_bd_green); - m_button_extruder_feed->SetTextColor(wxColour("#FFFFFE")); + m_button_extruder_feed->SetTextColor(btn_text_green); m_button_extruder_feed->SetFont(Label::Body_13); m_button_extruder_back = new Button(m_button_area, _L("Unload Filament")); m_button_extruder_back->SetBackgroundColor(btn_bg_white); m_button_extruder_back->SetBorderColor(btn_bd_white); + m_button_extruder_back->SetTextColor(btn_text_white); m_button_extruder_back->SetFont(Label::Body_13); m_sizer_button_area->Add(0, 0, 1, wxEXPAND, 0); @@ -1667,7 +1978,9 @@ AMSControl::AMSControl(wxWindow *parent, wxWindowID id, const wxPoint &pos, cons m_sizer_button_area->Add(m_button_extruder_back, 0, wxLEFT, FromDIP(6)); m_sizer_button_area->Add(m_button_extruder_feed, 0, wxLEFT, FromDIP(6)); - m_button_area->SetSizer(m_sizer_button_area); + m_sizer_button->Add(m_sizer_button_area, 0, 1, wxEXPAND, 0); + + m_button_area->SetSizer(m_sizer_button); m_button_area->Layout(); m_button_area->Fit(); @@ -1684,9 +1997,10 @@ AMSControl::AMSControl(wxWindow *parent, wxWindowID id, const wxPoint &pos, cons m_vams_info.material_state = AMSCanType::AMS_CAN_TYPE_VIRTUAL; m_vams_info.can_id = wxString::Format("%d", VIRTUAL_TRAY_ID).ToStdString(); + auto vams_panel = new wxWindow(m_panel_virtual, wxID_ANY); vams_panel->SetBackgroundColour(AMS_CONTROL_DEF_BLOCK_BK_COLOUR); - //m_vams_refresh = new AMSrefresh(vams_panel, wxID_ANY, 0, m_vams_info); + m_vams_lib = new AMSLib(vams_panel, wxID_ANY, m_vams_info); m_vams_road = new AMSRoad(vams_panel, wxID_ANY, m_vams_info, -1, -1, wxDefaultPosition, AMS_CAN_ROAD_SIZE); @@ -1705,20 +2019,25 @@ AMSControl::AMSControl(wxWindow *parent, wxWindowID id, const wxPoint &pos, cons }); Bind(EVT_AMS_UNSELETED_VAMS, [this](wxCommandEvent& e) { + if (m_current_ams == e.GetString().ToStdString()) { + return; + } m_current_ams = e.GetString().ToStdString(); SwitchAms(m_current_ams); m_vams_lib->UnSelected(); e.Skip(); }); - wxBoxSizer* m_sizer_vams = new wxBoxSizer(wxVERTICAL); - m_sizer_vams->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(14)); - //m_sizer_vams->Add(m_vams_refresh, 0, wxALIGN_CENTER_HORIZONTAL, 0); - m_sizer_vams->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(2) + AMS_REFRESH_SIZE.y); - m_sizer_vams->Add(m_vams_lib, 1, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, FromDIP(4)); - m_sizer_vams->Add(m_vams_road, 0, wxALL, 0); + wxBoxSizer* m_vams_top_sizer = new wxBoxSizer(wxVERTICAL); - vams_panel->SetSizer(m_sizer_vams); + m_vams_top_sizer->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(14)); + m_vams_top_sizer->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(2) + AMS_REFRESH_SIZE.y); + m_vams_top_sizer->Add(m_vams_lib, 1, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, FromDIP(4)); + m_vams_top_sizer->Add(m_vams_road, 0, wxALL, 0); + + //extra road + + vams_panel->SetSizer(m_vams_top_sizer); vams_panel->Layout(); vams_panel->Fit(); @@ -1728,11 +2047,10 @@ AMSControl::AMSControl(wxWindow *parent, wxWindowID id, const wxPoint &pos, cons m_panel_virtual->SetSizer(m_sizer_vams_panel); m_panel_virtual->Layout(); m_panel_virtual->Fit(); - //virtual ams m_vams_sizer = new wxBoxSizer(wxVERTICAL); - m_sizer_vams_tips = new wxBoxSizer(wxHORIZONTAL); + auto m_vams_tip = new wxStaticText(m_amswin, wxID_ANY, _L("Ext Spool")); m_vams_tip->SetFont(::Label::Body_12); m_vams_tip->SetBackgroundColour(*wxWHITE); @@ -1744,60 +2062,70 @@ AMSControl::AMSControl(wxWindow *parent, wxWindowID id, const wxPoint &pos, cons m_ams_introduce_popup.set_mode(false); m_ams_introduce_popup.Position(popup_pos, wxSize(0, 0)); m_ams_introduce_popup.Popup(); - }); + +#ifdef __WXMSW__ + wxCommandEvent close_event(EVT_CLEAR_SPEED_CONTROL); + wxPostEvent(this, close_event); +#endif // __WXMSW__ + }); img_vams_tip->Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& e) { m_ams_introduce_popup.Dismiss(); - }); + }); + m_sizer_vams_tips->Add(m_vams_tip, 0, wxTOP, FromDIP(5)); + m_sizer_vams_tips->Add(img_vams_tip, 0, wxALL, FromDIP(3)); - - m_sizer_vams_tips->Add(m_vams_tip, 0, wxALIGN_CENTER, 0); - m_sizer_vams_tips->Add(img_vams_tip, 0, wxALL, FromDIP(2)); + m_vams_extra_road = new AMSVirtualRoad(m_amswin, wxID_ANY); + m_vams_extra_road->SetMinSize(wxSize(m_panel_virtual->GetSize().x + FromDIP(16), -1)); m_vams_sizer->Add(m_sizer_vams_tips, 0, wxALIGN_CENTER, 0); m_vams_sizer->Add(m_panel_virtual, 0, wxALIGN_CENTER, 0); + m_vams_sizer->Add(m_vams_extra_road, 1, wxEXPAND, 0); - m_sizer_bottom->Add(m_vams_sizer, 0, wxEXPAND, 0); - m_sizer_bottom->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(10)); - m_sizer_bottom->Add(m_sizer_left, 0, wxEXPAND, 0); - m_sizer_bottom->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(23)); + //Right wxBoxSizer *m_sizer_right = new wxBoxSizer(wxVERTICAL); m_simplebook_right = new wxSimplebook(m_amswin, wxID_ANY); - m_simplebook_right->SetMinSize(AMS_STEP_SIZE); - m_simplebook_right->SetSize(AMS_STEP_SIZE); + m_simplebook_right->SetMinSize(wxSize(AMS_STEP_SIZE.x, AMS_STEP_SIZE.y + FromDIP(19))); + m_simplebook_right->SetMaxSize(wxSize(AMS_STEP_SIZE.x, AMS_STEP_SIZE.y + FromDIP(19))); m_simplebook_right->SetBackgroundColour(*wxWHITE); + m_sizer_right->Add(m_simplebook_right, 0, wxALL, 0); auto tip_right = new wxPanel(m_simplebook_right, wxID_ANY, wxDefaultPosition, AMS_STEP_SIZE, wxTAB_TRAVERSAL); m_sizer_right_tip = new wxBoxSizer(wxVERTICAL); + m_tip_right_top = new wxStaticText(tip_right, wxID_ANY, _L("Tips"), wxDefaultPosition, wxDefaultSize, 0); m_tip_right_top->SetFont(::Label::Head_13); m_tip_right_top->SetForegroundColour(AMS_CONTROL_BRAND_COLOUR); m_tip_right_top->Wrap(AMS_STEP_SIZE.x); - m_sizer_right_tip->Add(m_tip_right_top, 0, 0, 0); - m_sizer_right_tip->Add(0, 0, 0, wxTOP, FromDIP(10)); + + m_tip_load_info = new wxStaticText(tip_right, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); m_tip_load_info->SetFont(::Label::Body_13); m_tip_load_info->SetForegroundColour(AMS_CONTROL_GRAY700); + + m_sizer_right_tip->Add(m_tip_right_top, 0, 0, 0); + m_sizer_right_tip->Add(0, 0, 0, wxEXPAND, FromDIP(10)); m_sizer_right_tip->Add(m_tip_load_info, 0, 0, 0); + tip_right->SetSizer(m_sizer_right_tip); tip_right->Layout(); m_filament_load_step = new ::StepIndicator(m_simplebook_right, wxID_ANY); m_filament_load_step->SetMinSize(AMS_STEP_SIZE); - m_filament_load_step->SetSize(AMS_STEP_SIZE); + m_filament_load_step->SetMaxSize(AMS_STEP_SIZE); m_filament_load_step->SetBackgroundColour(*wxWHITE); m_filament_unload_step = new ::StepIndicator(m_simplebook_right, wxID_ANY); m_filament_unload_step->SetMinSize(AMS_STEP_SIZE); - m_filament_unload_step->SetSize(AMS_STEP_SIZE); + m_filament_unload_step->SetMaxSize(AMS_STEP_SIZE); m_filament_unload_step->SetBackgroundColour(*wxWHITE); m_filament_vt_load_step = new ::StepIndicator(m_simplebook_right, wxID_ANY); m_filament_vt_load_step->SetMinSize(AMS_STEP_SIZE); - m_filament_vt_load_step->SetSize(AMS_STEP_SIZE); + m_filament_vt_load_step->SetMaxSize(AMS_STEP_SIZE); m_filament_vt_load_step->SetBackgroundColour(*wxWHITE); m_simplebook_right->AddPage(tip_right, wxEmptyString, false); @@ -1818,6 +2146,7 @@ AMSControl::AMSControl(wxWindow *parent, wxWindowID id, const wxPoint &pos, cons m_button_guide->SetFont(Label::Body_13); m_button_guide->SetCornerRadius(FromDIP(12)); m_button_guide->SetBorderColor(btn_bd_white); + m_button_guide->SetTextColor(btn_text_white); m_button_guide->SetMinSize(wxSize(-1, FromDIP(24))); m_button_guide->SetBackgroundColor(btn_bg_white); @@ -1825,6 +2154,7 @@ AMSControl::AMSControl(wxWindow *parent, wxWindowID id, const wxPoint &pos, cons m_button_retry->SetFont(Label::Body_13); m_button_retry->SetCornerRadius(FromDIP(12)); m_button_retry->SetBorderColor(btn_bd_white); + m_button_retry->SetTextColor(btn_text_white); m_button_retry->SetMinSize(wxSize(-1, FromDIP(24))); m_button_retry->SetBackgroundColor(btn_bg_white); @@ -1832,7 +2162,12 @@ AMSControl::AMSControl(wxWindow *parent, wxWindowID id, const wxPoint &pos, cons m_sizer_right_bottom->Add(m_button_guide, 0, wxLEFT, FromDIP(10)); m_sizer_right_bottom->Add(m_button_retry, 0, wxLEFT, FromDIP(10)); m_sizer_right->Add(m_sizer_right_bottom, 0, wxEXPAND | wxTOP, FromDIP(20)); - m_sizer_bottom->Add(m_sizer_right, 0, wxEXPAND, FromDIP(5)); + + + m_sizer_bottom->Add(m_vams_sizer, 0, wxEXPAND, 0); + m_sizer_bottom->Add(m_sizer_left, 0, wxEXPAND, 0); + m_sizer_bottom->Add(0, 0, 0, wxLEFT, FromDIP(15)); + m_sizer_bottom->Add(m_sizer_right, 0, wxEXPAND, FromDIP(0)); m_sizer_body->Add(m_simplebook_amsitems, 0, wxEXPAND, 0); m_sizer_body->Add(0, 0, 1, wxEXPAND | wxTOP, FromDIP(18)); @@ -1917,7 +2252,7 @@ AMSControl::AMSControl(wxWindow *parent, wxWindowID id, const wxPoint &pos, cons AddPage(m_amswin, wxEmptyString, false); AddPage(m_simplebook_calibration, wxEmptyString, false); - UpdateStepCtrl(); + UpdateStepCtrl(false); m_button_extrusion_cali->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AMSControl::on_extrusion_cali), NULL, this); m_button_extruder_feed->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AMSControl::on_filament_load), NULL, this); @@ -1940,10 +2275,9 @@ AMSControl::AMSControl(wxWindow *parent, wxWindowID id, const wxPoint &pos, cons Bind(EVT_AMS_SHOW_HUMIDITY_TIPS, [this](wxCommandEvent& evt) { wxPoint img_pos = ClientToScreen(wxPoint(0, 0)); - wxPoint popup_pos(img_pos.x, img_pos.y + GetRect().height); + wxPoint popup_pos(img_pos.x - m_Humidity_tip_popup.GetSize().GetWidth() + FromDIP(150), img_pos.y); m_Humidity_tip_popup.Position(popup_pos, wxSize(0, 0)); m_Humidity_tip_popup.Popup(); - m_Humidity_tip_popup.GetParent()->SetFocus(); }); @@ -1970,6 +2304,7 @@ void AMSControl::init_scaled_buttons() } std::string AMSControl::GetCurentAms() { return m_current_ams; } +std::string AMSControl::GetCurentShowAms() { return m_current_show_ams; } std::string AMSControl::GetCurrentCan(std::string amsid) { @@ -1982,13 +2317,6 @@ std::string AMSControl::GetCurrentCan(std::string amsid) } } return current_can; - /*std::string current_can_id = ""; - for (auto i = 0; i < m_ams_info.size(); i++) { - if (m_ams_info[i].ams_id == m_current_ams) { - current_can_id = m_ams_info[i].current_can_id; - } - } - return current_can_id;*/ } wxColour AMSControl::GetCanColour(std::string amsid, std::string canid) @@ -2024,7 +2352,7 @@ void AMSControl::EnterNoneAMSMode(bool support_vt_load) m_panel_top->Hide(); m_simplebook_amsitems->SetSelection(1); m_simplebook_ams->SetSelection(1); - m_extruder->Hide(); + m_extruder->no_ams_mode(true); m_button_ams_setting->Hide(); m_button_guide->Hide(); m_button_retry->Hide(); @@ -2049,7 +2377,7 @@ void AMSControl::ExitNoneAMSMode() m_panel_top->Show(); m_simplebook_ams->SetSelection(0); m_simplebook_amsitems->SetSelection(0); - m_extruder->Show(); + m_extruder->no_ams_mode(false); m_button_ams_setting->Show(); m_button_guide->Show(); m_button_retry->Show(); @@ -2115,6 +2443,8 @@ void AMSControl::msw_rescale() m_button_ams_setting->SetBitmap(m_button_ams_setting_normal.bmp()); m_extruder->msw_rescale(); + m_vams_extra_road->msw_rescale(); + m_button_extrusion_cali->SetMinSize(wxSize(-1, FromDIP(24))); m_button_extruder_feed->SetMinSize(wxSize(-1, FromDIP(24))); m_button_extruder_back->SetMinSize(wxSize(-1, FromDIP(24))); @@ -2131,15 +2461,35 @@ void AMSControl::msw_rescale() Refresh(); } -void AMSControl::UpdateStepCtrl() +void AMSControl::UpdateStepCtrl(bool is_extrusion) { - wxString FILAMENT_LOAD_STEP_STRING[LOAD_STEP_COUNT] = { - _L("Heat the nozzle"), - _L("Cut filament"), - _L("Pull back current filament"), - _L("Push new filament into extruder"), - _L("Purge old filament"), - }; + m_filament_load_step->DeleteAllItems(); + m_filament_unload_step->DeleteAllItems(); + m_filament_vt_load_step->DeleteAllItems(); + + if(is_extrusion){ + wxString FILAMENT_LOAD_STEP_STRING[LOAD_STEP_COUNT] = { + _L("Heat the nozzle"), + _L("Cut filament"), + _L("Pull back current filament"), + _L("Push new filament into extruder"), + _L("Purge old filament"), + }; + + for (int i = 0; i < LOAD_STEP_COUNT; i++) { + m_filament_load_step->AppendItem(FILAMENT_LOAD_STEP_STRING[i]); + } + }else{ + wxString FILAMENT_LOAD_STEP_STRING[LOAD_WITH_TEMP_STEP_COUNT] = { + _L("Heat the nozzle"), + _L("Push new filament into extruder"), + _L("Purge old filament"), + }; + + for (int i = 0; i < LOAD_WITH_TEMP_STEP_COUNT; i++) { + m_filament_load_step->AppendItem(FILAMENT_LOAD_STEP_STRING[i]); + } + } wxString VT_TRAY_LOAD_STEP_STRING[VT_LOAD_STEP_COUNT] = { _L("Heat the nozzle"), @@ -2154,9 +2504,9 @@ void AMSControl::UpdateStepCtrl() _L("Pull back current filament") }; - for (int i = 0; i < LOAD_STEP_COUNT; i++) { - m_filament_load_step->AppendItem(FILAMENT_LOAD_STEP_STRING[i]); - } +// for (int i = 0; i < LOAD_STEP_COUNT; i++) { +// m_filament_load_step->AppendItem(FILAMENT_LOAD_STEP_STRING[i]); +// } for (int i = 0; i < UNLOAD_STEP_COUNT; i++) { m_filament_unload_step->AppendItem(FILAMENT_UNLOAD_STEP_STRING[i]); } @@ -2186,28 +2536,30 @@ void AMSControl::CreateAms() void AMSControl::Reset() { - auto caninfo0_0 = Caninfo{"def_can_0", "", *wxWHITE, AMSCanType::AMS_CAN_TYPE_NONE}; - auto caninfo0_1 = Caninfo{"def_can_1", "", *wxWHITE, AMSCanType::AMS_CAN_TYPE_NONE}; - auto caninfo0_2 = Caninfo{"def_can_2", "", *wxWHITE, AMSCanType::AMS_CAN_TYPE_NONE}; - auto caninfo0_3 = Caninfo{"def_can_3", "", *wxWHITE, AMSCanType::AMS_CAN_TYPE_NONE}; + auto caninfo0_0 = Caninfo{"0", "", *wxWHITE, AMSCanType::AMS_CAN_TYPE_NONE}; + auto caninfo0_1 = Caninfo{"1", "", *wxWHITE, AMSCanType::AMS_CAN_TYPE_NONE}; + auto caninfo0_2 = Caninfo{"2", "", *wxWHITE, AMSCanType::AMS_CAN_TYPE_NONE}; + auto caninfo0_3 = Caninfo{"3", "", *wxWHITE, AMSCanType::AMS_CAN_TYPE_NONE}; - AMSinfo ams1 = AMSinfo{"def_ams_0", std::vector{caninfo0_0, caninfo0_1, caninfo0_2, caninfo0_3}}; - AMSinfo ams2 = AMSinfo{"def_ams_1", std::vector{caninfo0_0, caninfo0_1, caninfo0_2, caninfo0_3}}; - AMSinfo ams3 = AMSinfo{"def_ams_2", std::vector{caninfo0_0, caninfo0_1, caninfo0_2, caninfo0_3}}; - AMSinfo ams4 = AMSinfo{"def_ams_3", std::vector{caninfo0_0, caninfo0_1, caninfo0_2, caninfo0_3}}; + AMSinfo ams1 = AMSinfo{"0", std::vector{caninfo0_0, caninfo0_1, caninfo0_2, caninfo0_3}}; + AMSinfo ams2 = AMSinfo{"1", std::vector{caninfo0_0, caninfo0_1, caninfo0_2, caninfo0_3}}; + AMSinfo ams3 = AMSinfo{"2", std::vector{caninfo0_0, caninfo0_1, caninfo0_2, caninfo0_3}}; + AMSinfo ams4 = AMSinfo{"3", std::vector{caninfo0_0, caninfo0_1, caninfo0_2, caninfo0_3}}; std::vector ams_info{ams1, ams2, ams3, ams4}; std::vector::iterator it; - UpdateAms(ams_info, false); - m_current_ams = ""; - m_current_senect = ""; + UpdateAms(ams_info, false, false, true); + m_current_show_ams = ""; + m_current_ams = ""; + m_current_senect = ""; } -void AMSControl::show_noams_mode(bool show, bool support_virtual_tray, bool support_vt_load) +void AMSControl::show_noams_mode(bool show, bool support_virtual_tray, bool support_extrustion_cali, bool support_vt_load) { show_vams(support_virtual_tray); m_sizer_ams_tips->Show(support_virtual_tray); - if (!support_virtual_tray) + + if (!support_extrustion_cali) m_button_extrusion_cali->Hide(); else { m_button_extrusion_cali->Show(); @@ -2220,6 +2572,8 @@ void AMSControl::show_vams(bool show) { m_panel_virtual->Show(show); m_vams_sizer->Show(show); + m_vams_extra_road->Show(show); + m_extruder->has_ams(show); show_vams_kn_value(show); Layout(); @@ -2237,8 +2591,9 @@ void AMSControl::show_vams_kn_value(bool show) m_vams_lib->show_kn_value(show); } -void AMSControl::update_vams_kn_value(AmsTray tray) +void AMSControl::update_vams_kn_value(AmsTray tray, MachineObject* obj) { + m_vams_lib->m_obj = obj; m_vams_info.k = tray.k; m_vams_info.n = tray.n; m_vams_lib->m_info.k = tray.k; @@ -2250,7 +2605,23 @@ void AMSControl::update_vams_kn_value(AmsTray tray) m_vams_lib->Refresh(); } -void AMSControl::UpdateAms(std::vector info, bool keep_selection, bool has_extrusion_cali) +void AMSControl::show_filament_backup(bool show) +{ +} + +void AMSControl::reset_vams() +{ + m_vams_lib->m_info.k = 0; + m_vams_lib->m_info.n = 0; + m_vams_lib->m_info.material_name = wxEmptyString; + m_vams_lib->m_info.material_colour = AMS_CONTROL_WHITE_COLOUR; + m_vams_info.material_name = wxEmptyString; + m_vams_info.material_colour = AMS_CONTROL_WHITE_COLOUR; + m_vams_lib->Refresh(); +} + + +void AMSControl::UpdateAms(std::vector info, bool keep_selection, bool has_extrusion_cali, bool is_reset) { std::string curr_ams_id = GetCurentAms(); std::string curr_can_id = GetCurrentCan(curr_ams_id); @@ -2288,14 +2659,17 @@ void AMSControl::UpdateAms(std::vector info, bool keep_selection, bool // update cans for (auto i = 0; i < m_ams_cans_list.GetCount(); i++) { AmsCansWindow *cans = m_ams_cans_list[i]; - if (i < info.size()) { - cans->amsCans->m_info = m_ams_info[i]; - cans->amsCans->Update(m_ams_info[i]); - cans->amsCans->show_sn_value(has_extrusion_cali); + + for (auto ifo : m_ams_info) { + if (ifo.ams_id == cans->amsIndex) { + cans->amsCans->m_info = ifo; + cans->amsCans->Update(ifo); + cans->amsCans->show_sn_value(has_extrusion_cali); + } } } - if (m_current_senect.empty() && info.size() > 0) { + /*if (m_current_senect.empty() && info.size() > 0) { if (curr_ams_id.empty()) { SwitchAms(info[0].ams_id); return; @@ -2309,6 +2683,12 @@ void AMSControl::UpdateAms(std::vector info, bool keep_selection, bool } return; } + }*/ + + if ( m_current_show_ams.empty() && !is_reset ) { + if (info.size() > 0) { + SwitchAms(info[0].ams_id); + } } } @@ -2347,22 +2727,27 @@ void AMSControl::AddAms(AMSinfo info, bool refresh) void AMSControl::SwitchAms(std::string ams_id) { + if (ams_id != std::to_string(VIRTUAL_TRAY_ID)) { + if (m_current_show_ams != ams_id) { + m_current_show_ams = ams_id; + m_extruder->OnAmsLoading(false); + } + } + for (auto i = 0; i < m_ams_item_list.GetCount(); i++) { AmsItems *item = m_ams_item_list[i]; - if (item->amsItem->m_amsinfo.ams_id == ams_id) { + if (item->amsItem->m_amsinfo.ams_id == m_current_show_ams) { item->amsItem->OnSelected(); - //item->amsItem->ShowHumidity(); + m_current_senect = ams_id; - if (m_current_ams == std::to_string(VIRTUAL_TRAY_ID)) { - for (auto i = 0; i < m_ams_cans_list.GetCount(); i++) { - AmsCansWindow* ams = m_ams_cans_list[i]; - if (ams->amsCans->m_info.ams_id == ams_id) { - ams->amsCans->SetDefSelectCan(); - } + for (auto i = 0; i < m_ams_cans_list.GetCount(); i++) { + AmsCansWindow* ams = m_ams_cans_list[i]; + if (ams->amsCans->m_info.ams_id == ams_id) { + ams->amsCans->SetDefSelectCan(); + m_vams_lib->UnSelected(); } } - m_current_senect = ams_id; } else { item->amsItem->UnSelected(); //item->amsItem->HideHumidity(); @@ -2373,11 +2758,16 @@ void AMSControl::SwitchAms(std::string ams_id) for (auto i = 0; i < m_ams_cans_list.GetCount(); i++) { AmsCansWindow *cans = m_ams_cans_list[i]; - if (cans->amsCans->m_info.ams_id == ams_id) { m_simplebook_cans->SetSelection(cans->amsCans->m_selection); } + if (cans->amsCans->m_info.ams_id == ams_id) { + m_simplebook_cans->SetSelection(cans->amsCans->m_selection); + } } + m_current_ams = ams_id; + // update extruder + //m_extruder->OnAmsLoading(false); for (auto i = 0; i < m_ams_info.size(); i++) { if (m_ams_info[i].ams_id == m_current_ams) { switch (m_ams_info[i].current_step) { @@ -2391,11 +2781,9 @@ void AMSControl::SwitchAms(std::string ams_id) } } } - - // update buttons } -void AMSControl::SetFilamentStep(int item_idx, FilamentStepType f_type) +void AMSControl::SetFilamentStep(int item_idx, FilamentStepType f_type, bool is_extrusion_exist) { if (item_idx == FilamentStep::STEP_IDLE) { m_simplebook_right->SetSelection(0); @@ -2492,12 +2880,28 @@ bool AMSControl::Enable(bool enable) return wxWindow::Enable(enable); } -void AMSControl::SetExtruder(bool on_off, wxColour col) +void AMSControl::SetExtruder(bool on_off, bool is_vams, wxColour col) { if (!on_off) { m_extruder->TurnOff(); + m_vams_extra_road->OnVamsLoading(false); + m_extruder->OnVamsLoading(false); + m_vams_road->OnVamsLoading(false); } else { m_extruder->TurnOn(col); + m_extruder->OnAmsLoading(true, col); + } + + if (is_vams && on_off) { + m_extruder->OnAmsLoading(false, col); + m_vams_extra_road->OnVamsLoading(true, col); + m_extruder->OnVamsLoading(true, col); + m_vams_road->OnVamsLoading(true, col); + } + else { + m_vams_extra_road->OnVamsLoading(false, col); + m_extruder->OnVamsLoading(false, col); + m_vams_road->OnVamsLoading(false, col); } } @@ -2514,26 +2918,45 @@ void AMSControl::SetAmsStep(std::string ams_id, std::string canid, AMSPassRoadTy } } + + if (ams_id != m_last_ams_id || m_last_tray_id != canid) { + SetAmsStep(m_last_ams_id, m_last_tray_id, AMSPassRoadType::AMS_ROAD_TYPE_UNLOAD, AMSPassRoadSTEP::AMS_ROAD_STEP_NONE); + m_vams_extra_road->OnVamsLoading(false); + m_extruder->OnVamsLoading(false); + m_vams_road->OnVamsLoading(false); + } + if (notfound) return; if (cans == nullptr) return; + + m_last_ams_id = ams_id; + m_last_tray_id = canid; + + if (step == AMSPassRoadSTEP::AMS_ROAD_STEP_NONE) { cans->amsCans->SetAmsStep(canid, type, AMSPassRoadSTEP::AMS_ROAD_STEP_NONE); + m_extruder->OnAmsLoading(false); } if (step == AMSPassRoadSTEP::AMS_ROAD_STEP_COMBO_LOAD_STEP1) { cans->amsCans->SetAmsStep(canid, type, AMSPassRoadSTEP::AMS_ROAD_STEP_1); + m_extruder->OnAmsLoading(false); } if (step == AMSPassRoadSTEP::AMS_ROAD_STEP_COMBO_LOAD_STEP2) { cans->amsCans->SetAmsStep(canid, type, AMSPassRoadSTEP::AMS_ROAD_STEP_1); cans->amsCans->SetAmsStep(canid, type, AMSPassRoadSTEP::AMS_ROAD_STEP_2); + if (m_current_show_ams == ams_id) { + m_extruder->OnAmsLoading(true, cans->amsCans->GetTagColr(canid)); + } } if (step == AMSPassRoadSTEP::AMS_ROAD_STEP_COMBO_LOAD_STEP3) { cans->amsCans->SetAmsStep(canid, type, AMSPassRoadSTEP::AMS_ROAD_STEP_1); cans->amsCans->SetAmsStep(canid, type, AMSPassRoadSTEP::AMS_ROAD_STEP_2); cans->amsCans->SetAmsStep(canid, type, AMSPassRoadSTEP::AMS_ROAD_STEP_3); + m_extruder->OnAmsLoading(true, cans->amsCans->GetTagColr(canid)); } for (auto i = 0; i < m_ams_info.size(); i++) { diff --git a/src/slic3r/GUI/Widgets/AMSControl.hpp b/src/slic3r/GUI/Widgets/AMSControl.hpp index 18bdd8bba1..daa2debb8e 100644 --- a/src/slic3r/GUI/Widgets/AMSControl.hpp +++ b/src/slic3r/GUI/Widgets/AMSControl.hpp @@ -49,6 +49,7 @@ enum class AMSRoadMode : int { AMS_ROAD_MODE_END_ONLY, AMS_ROAD_MODE_NONE, AMS_ROAD_MODE_NONE_ANY_ROAD, + AMS_ROAD_MODE_VIRTUAL_TRAY }; enum class AMSPassRoadMode : int { @@ -121,9 +122,9 @@ enum FilamentStepType { #define AMS_CAN_ITEM_HEIGHT_SIZE FromDIP(27) #define AMS_CANS_SIZE wxSize(FromDIP(284), FromDIP(186)) #define AMS_CANS_WINDOW_SIZE wxSize(FromDIP(264), FromDIP(186)) -#define AMS_STEP_SIZE wxSize(FromDIP(172), FromDIP(180)) +#define AMS_STEP_SIZE wxSize(FromDIP(172), FromDIP(186)) #define AMS_REFRESH_SIZE wxSize(FromDIP(30), FromDIP(30)) -#define AMS_EXTRUDER_SIZE wxSize(FromDIP(66), FromDIP(55)) +#define AMS_EXTRUDER_SIZE wxSize(FromDIP(86), FromDIP(72)) #define AMS_EXTRUDER_BITMAP_SIZE wxSize(FromDIP(36), FromDIP(55)) struct Caninfo @@ -135,6 +136,7 @@ struct Caninfo int material_remain = 100; float k = 0.0f; float n = 0.0f; + std::vector material_cols; }; struct AMSinfo @@ -229,7 +231,20 @@ public: void TurnOn(wxColour col); void TurnOff(); void create(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size); + void OnVamsLoading(bool load, wxColour col = AMS_CONTROL_GRAY500); + void OnAmsLoading(bool load, wxColour col = AMS_CONTROL_GRAY500); + void paintEvent(wxPaintEvent& evt); + void render(wxDC& dc); + void doRender(wxDC& dc); void msw_rescale(); + void has_ams(bool hams) {m_has_vams = hams; Refresh();}; + void no_ams_mode(bool mode) {m_none_ams_mode = mode; Refresh();}; + + bool m_none_ams_mode{false}; + bool m_has_vams{false}; + bool m_vams_loading{false}; + bool m_ams_loading{false}; + wxColour m_current_colur; wxBoxSizer * m_bitmap_sizer{nullptr}; wxPanel * m_bitmap_panel{nullptr}; @@ -239,6 +254,27 @@ public: ~AMSextruder(); }; +class AMSVirtualRoad : public wxWindow +{ +public: + AMSVirtualRoad(wxWindow* parent, wxWindowID id, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize); + ~AMSVirtualRoad(); + +private: + bool m_has_vams{ true }; + bool m_vams_loading{ false }; + wxColour m_current_color; + +public: + void OnVamsLoading(bool load, wxColour col = AMS_CONTROL_GRAY500); + void SetHasVams(bool hvams) { m_has_vams = hvams; }; + void create(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size); + void paintEvent(wxPaintEvent& evt); + void render(wxDC& dc); + void doRender(wxDC& dc); + void msw_rescale(); +}; + /************************************************* Description:AMSLib **************************************************/ @@ -248,17 +284,21 @@ public: AMSLib(wxWindow *parent, wxWindowID id, Caninfo info, const wxPoint &pos = wxDefaultPosition, const wxSize &size = wxDefaultSize); void create(wxWindow *parent, wxWindowID id = wxID_ANY, const wxPoint &pos = wxDefaultPosition, const wxSize &size = wxDefaultSize); public: + wxColour GetLibColour(); + Caninfo m_info; + MachineObject* m_obj = {nullptr}; int m_can_index; void Update(Caninfo info, bool refresh = true); void UnableSelected() { m_unable_selected = true; }; void EableSelected() { m_unable_selected = false; }; - wxColour GetLibColour(); void OnSelected(); void UnSelected(); - virtual bool Enable(bool enable = true); + bool is_selected() {return m_selected;}; void post_event(wxCommandEvent &&event); - Caninfo m_info; void show_kn_value(bool show) { m_show_kn = show; }; + void support_cali(bool sup) { m_support_cali = sup; Refresh(); }; + virtual bool Enable(bool enable = true); + protected: wxStaticBitmap *m_edit_bitmp = {nullptr}; @@ -272,6 +312,7 @@ protected: bool m_selected = {false}; bool m_hover = {false}; bool m_show_kn = {false}; + bool m_support_cali = {false}; double m_radius = {4}; wxColour m_border_color; @@ -314,9 +355,12 @@ public: ScalableBitmap ams_humidity_2; ScalableBitmap ams_humidity_3; ScalableBitmap ams_humidity_4; - bool m_show_humidity = { false }; - int m_humidity = { 0 }; + int m_humidity = { 0 }; + bool m_show_humidity = { false }; + bool m_vams_loading{false}; + + void OnVamsLoading(bool load, wxColour col = AMS_CONTROL_GRAY500); void SetPassRoadColour(wxColour col); void SetMode(AMSRoadMode mode); void OnPassRoad(std::vector prord_list); @@ -407,8 +451,9 @@ public: void Update(AMSinfo info); void create(wxWindow *parent, wxWindowID id, AMSinfo info, const wxPoint &pos, const wxSize &size); void AddCan(Caninfo caninfo, int canindex, int maxcan); - void SetDefSelectCan(); + void SetDefSelectCan(); void SelectCan(std::string canid); + wxColour GetTagColr(wxString canid); void SetAmsStep(wxString canid, AMSPassRoadType type, AMSPassRoadSTEP step); //wxColour GetCanColour(wxString canid); void PlayRridLoading(wxString canid); @@ -473,6 +518,8 @@ protected: std::vector m_ams_info; std::string m_current_ams; + std::string m_current_show_ams; + AmsItemsHash m_ams_item_list; AmsCansHash m_ams_cans_list; @@ -496,11 +543,11 @@ protected: wxBoxSizer* m_vams_sizer = {nullptr}; wxBoxSizer* m_sizer_vams_tips = {nullptr}; - Caninfo m_vams_info; - StaticBox* m_panel_virtual = {nullptr}; - AMSrefresh* m_vams_refresh = {nullptr}; - AMSLib* m_vams_lib = {nullptr}; - AMSRoad* m_vams_road = {nullptr}; + Caninfo m_vams_info; + StaticBox* m_panel_virtual = {nullptr}; + AMSLib* m_vams_lib = {nullptr}; + AMSRoad* m_vams_road = {nullptr}; + AMSVirtualRoad* m_vams_extra_road = {nullptr}; StaticBox * m_panel_can = {nullptr}; wxBoxSizer *m_sizer_top = {nullptr}; @@ -525,8 +572,12 @@ protected: wxHyperlinkCtrl *m_hyperlink = {nullptr}; AmsHumidityTipPopup m_Humidity_tip_popup; + + std::string m_last_ams_id; + std::string m_last_tray_id; public: std::string GetCurentAms(); + std::string GetCurentShowAms(); std::string GetCurrentCan(std::string amsid); wxColour GetCanColour(std::string amsid, std::string canid); @@ -544,15 +595,15 @@ public: void PlayRridLoading(wxString amsid, wxString canid); void StopRridLoading(wxString amsid, wxString canid); - void SetFilamentStep(int item_idx, FilamentStepType f_type); + void SetFilamentStep(int item_idx, FilamentStepType f_type, bool is_extrusion_exist = false); void ShowFilamentTip(bool hasams = true); void SetHumidity(std::string amsid, int humidity); - void UpdateStepCtrl(); + void UpdateStepCtrl(bool is_extrusion_exist); void CreateAms(); - void UpdateAms(std::vector info, bool keep_selection = true, bool has_extrusion_cali = true); + void UpdateAms(std::vector info, bool keep_selection = true, bool has_extrusion_cali = true, bool is_reset = false); void AddAms(AMSinfo info, bool refresh = true); - void SetExtruder(bool on_off, wxColour col); + void SetExtruder(bool on_off, bool is_vams, wxColour col); void SetAmsStep(std::string ams_id, std::string canid, AMSPassRoadType type, AMSPassRoadSTEP step); void SwitchAms(std::string ams_id); @@ -566,12 +617,14 @@ public: void on_clibration_cancel_click(wxMouseEvent &event); void Reset(); - void show_noams_mode(bool show, bool support_virtual_tray, bool support_vt_load = false); + void show_noams_mode(bool show, bool support_virtual_tray, bool support_extrustion_cali, bool support_vt_load = false); void show_vams(bool show); void show_vams_kn_value(bool show); - void update_vams_kn_value(AmsTray tray); + void update_vams_kn_value(AmsTray tray, MachineObject* obj); + void show_filament_backup(bool show); - void post_event(wxEvent &&event); + void reset_vams(); + void post_event(wxEvent&& event); virtual bool Enable(bool enable = true); @@ -583,6 +636,7 @@ wxDECLARE_EVENT(EVT_AMS_EXTRUSION_CALI, wxCommandEvent); wxDECLARE_EVENT(EVT_AMS_LOAD, SimpleEvent); wxDECLARE_EVENT(EVT_AMS_UNLOAD, SimpleEvent); wxDECLARE_EVENT(EVT_AMS_SETTINGS, SimpleEvent); +wxDECLARE_EVENT(EVT_AMS_FILAMENT_BACKUP, SimpleEvent); wxDECLARE_EVENT(EVT_AMS_REFRESH_RFID, wxCommandEvent); wxDECLARE_EVENT(EVT_AMS_ON_SELECTED, wxCommandEvent); wxDECLARE_EVENT(EVT_AMS_ON_FILAMENT_EDIT, wxCommandEvent); @@ -593,6 +647,7 @@ wxDECLARE_EVENT(EVT_AMS_GUIDE_WIKI, wxCommandEvent); wxDECLARE_EVENT(EVT_AMS_RETRY, wxCommandEvent); wxDECLARE_EVENT(EVT_AMS_SHOW_HUMIDITY_TIPS, wxCommandEvent); wxDECLARE_EVENT(EVT_AMS_UNSELETED_VAMS, wxCommandEvent); +wxDECLARE_EVENT(EVT_CLEAR_SPEED_CONTROL, wxCommandEvent); }} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Widgets/Button.cpp b/src/slic3r/GUI/Widgets/Button.cpp index 0792e188f9..7e49b303a4 100644 --- a/src/slic3r/GUI/Widgets/Button.cpp +++ b/src/slic3r/GUI/Widgets/Button.cpp @@ -36,15 +36,15 @@ Button::Button() std::make_pair(*wxBLACK, (int) StateColor::Normal)); } -Button::Button(wxWindow* parent, wxString text, wxString icon, long style, int iconSize) +Button::Button(wxWindow* parent, wxString text, wxString icon, long style, int iconSize, wxWindowID btn_id) : Button() { - Create(parent, text, icon, style, iconSize); + Create(parent, text, icon, style, iconSize, btn_id); } -bool Button::Create(wxWindow* parent, wxString text, wxString icon, long style, int iconSize) +bool Button::Create(wxWindow* parent, wxString text, wxString icon, long style, int iconSize, wxWindowID btn_id) { - StaticBox::Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, style); + StaticBox::Create(parent, btn_id, wxDefaultPosition, wxDefaultSize, style); state_handler.attach({&text_color}); state_handler.update_binds(); //BBS set default font @@ -65,6 +65,14 @@ void Button::SetLabel(const wxString& label) Refresh(); } +bool Button::SetFont(const wxFont& font) +{ + wxWindow::SetFont(font); + messureSize(); + Refresh(); + return true; +} + void Button::SetIcon(const wxString& icon) { if (!icon.IsEmpty()) { @@ -166,7 +174,7 @@ void Button::render(wxDC& dc) dc.SetBrush(*wxTRANSPARENT_BRUSH); // calc content size wxSize szIcon; - wxSize szContent = textSize; + wxSize szContent = textSize.GetSize(); ScalableBitmap icon; if (m_selected || ((states & (int)StateColor::State::Hovered) != 0)) @@ -205,11 +213,18 @@ void Button::render(wxDC& dc) } auto text = GetLabel(); if (!text.IsEmpty()) { - if (pt.x + textSize.x > size.x) + if (pt.x + textSize.width > size.x) text = wxControl::Ellipsize(text, dc, wxELLIPSIZE_END, size.x - pt.x); - pt.y += (rcContent.height - textSize.y) / 2; - dc.SetFont(GetFont()); + pt.y += (rcContent.height - textSize.height) / 2; dc.SetTextForeground(text_color.colorForStates(states)); +#if 0 + dc.SetBrush(*wxLIGHT_GREY); + dc.SetPen(wxPen(*wxLIGHT_GREY)); + dc.DrawRectangle(pt, textSize.GetSize()); +#endif +#ifdef __WXOSX__ + pt.y -= textSize.x / 2; +#endif dc.DrawText(text, pt); } } @@ -217,12 +232,8 @@ void Button::render(wxDC& dc) void Button::messureSize() { wxClientDC dc(this); - textSize = dc.GetTextExtent(GetLabel()); - if (minSize.GetWidth() > 0) { - wxWindow::SetMinSize(minSize); - return; - } - wxSize szContent = textSize; + dc.GetTextExtent(GetLabel(), &textSize.width, &textSize.height, &textSize.x, &textSize.y); + wxSize szContent = textSize.GetSize(); if (this->active_icon.bmp().IsOk()) { if (szContent.y > 0) { //BBS norrow size between text and icon @@ -236,7 +247,11 @@ void Button::messureSize() wxSize size = szContent + paddingSize * 2; if (minSize.GetHeight() > 0) size.SetHeight(minSize.GetHeight()); - wxWindow::SetMinSize(size); + + if (minSize.GetWidth() > size.GetWidth()) + wxWindow::SetMinSize(minSize); + else + wxWindow::SetMinSize(size); } void Button::mouseDown(wxMouseEvent& event) diff --git a/src/slic3r/GUI/Widgets/Button.hpp b/src/slic3r/GUI/Widgets/Button.hpp index 56e46ca8f3..2f5c8eaea0 100644 --- a/src/slic3r/GUI/Widgets/Button.hpp +++ b/src/slic3r/GUI/Widgets/Button.hpp @@ -6,7 +6,7 @@ class Button : public StaticBox { - wxSize textSize; + wxRect textSize; wxSize minSize; // set by outer wxSize paddingSize; ScalableBitmap active_icon; @@ -24,12 +24,14 @@ class Button : public StaticBox public: Button(); - Button(wxWindow* parent, wxString text, wxString icon = "", long style = 0, int iconSize = 0); + Button(wxWindow* parent, wxString text, wxString icon = "", long style = 0, int iconSize = 0, wxWindowID btn_id = wxID_ANY); - bool Create(wxWindow* parent, wxString text, wxString icon = "", long style = 0, int iconSize = 0); + bool Create(wxWindow* parent, wxString text, wxString icon = "", long style = 0, int iconSize = 0, wxWindowID btn_id = wxID_ANY); void SetLabel(const wxString& label) override; + bool SetFont(const wxFont& font) override; + void SetIcon(const wxString& icon); void SetInactiveIcon(const wxString& icon); diff --git a/src/slic3r/GUI/Widgets/CheckBox.cpp b/src/slic3r/GUI/Widgets/CheckBox.cpp index 4ca1ca5ba0..8a9b05b50b 100644 --- a/src/slic3r/GUI/Widgets/CheckBox.cpp +++ b/src/slic3r/GUI/Widgets/CheckBox.cpp @@ -2,8 +2,8 @@ #include "../wxExtensions.hpp" -CheckBox::CheckBox(wxWindow* parent) - : wxBitmapToggleButton(parent, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE) +CheckBox::CheckBox(wxWindow *parent, int id) + : wxBitmapToggleButton(parent, id, wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE) , m_on(this, "check_on", 18) , m_half(this, "check_half", 18) , m_off(this, "check_off", 18) diff --git a/src/slic3r/GUI/Widgets/CheckBox.hpp b/src/slic3r/GUI/Widgets/CheckBox.hpp index 53c41b6470..01b801a215 100644 --- a/src/slic3r/GUI/Widgets/CheckBox.hpp +++ b/src/slic3r/GUI/Widgets/CheckBox.hpp @@ -8,7 +8,7 @@ class CheckBox : public wxBitmapToggleButton { public: - CheckBox(wxWindow * parent = NULL); + CheckBox(wxWindow * parent, int id = wxID_ANY); public: void SetValue(bool value) override; diff --git a/src/slic3r/GUI/Widgets/ComboBox.cpp b/src/slic3r/GUI/Widgets/ComboBox.cpp index 4c89398c23..56004775d2 100644 --- a/src/slic3r/GUI/Widgets/ComboBox.cpp +++ b/src/slic3r/GUI/Widgets/ComboBox.cpp @@ -6,6 +6,7 @@ BEGIN_EVENT_TABLE(ComboBox, TextInput) EVT_LEFT_DOWN(ComboBox::mouseDown) +EVT_LEFT_DCLICK(ComboBox::mouseDown) //EVT_MOUSEWHEEL(ComboBox::mouseWheelMoved) EVT_KEY_DOWN(ComboBox::keyDown) diff --git a/src/slic3r/GUI/Widgets/DropDown.cpp b/src/slic3r/GUI/Widgets/DropDown.cpp index ca586dff39..1ff119db65 100644 --- a/src/slic3r/GUI/Widgets/DropDown.cpp +++ b/src/slic3r/GUI/Widgets/DropDown.cpp @@ -157,7 +157,7 @@ bool DropDown::HasDismissLongTime() { auto now = boost::posix_time::microsec_clock::universal_time(); return !IsShown() && - (now - dismissTime).total_milliseconds() >= 200; + (now - dismissTime).total_milliseconds() >= 20; } void DropDown::paintEvent(wxPaintEvent& evt) diff --git a/src/slic3r/GUI/Widgets/FanControl.cpp b/src/slic3r/GUI/Widgets/FanControl.cpp index da0a414b22..57f4217fff 100644 --- a/src/slic3r/GUI/Widgets/FanControl.cpp +++ b/src/slic3r/GUI/Widgets/FanControl.cpp @@ -394,7 +394,7 @@ void FanControl::command_control_fan() { if (m_current_speed < 0 || m_current_speed > 10) { return; } int speed = floor(m_current_speed * float(25.5)); - if (m_update_already) { + if (m_update_already && m_obj) { m_obj->command_control_fan_val(m_type, speed); post_event(wxCommandEvent(EVT_FAN_CHANGED)); } diff --git a/src/slic3r/GUI/Widgets/Label.cpp b/src/slic3r/GUI/Widgets/Label.cpp index 10b404f815..1dd8b616d5 100644 --- a/src/slic3r/GUI/Widgets/Label.cpp +++ b/src/slic3r/GUI/Widgets/Label.cpp @@ -34,6 +34,7 @@ wxFont Label::Head_15; wxFont Label::Head_14; wxFont Label::Head_13; wxFont Label::Head_12; +wxFont Label::Head_11; wxFont Label::Head_10; wxFont Label::Body_16; @@ -69,6 +70,7 @@ void Label::initSysFont() Head_14 = Label::sysFont(14, true); Head_13 = Label::sysFont(13, true); Head_12 = Label::sysFont(12, true); + Head_11 = Label::sysFont(11, true); Head_10 = Label::sysFont(10, true); Body_16 = Label::sysFont(16, false); diff --git a/src/slic3r/GUI/Widgets/Label.hpp b/src/slic3r/GUI/Widgets/Label.hpp index 8d74dc6886..95db3c62b1 100644 --- a/src/slic3r/GUI/Widgets/Label.hpp +++ b/src/slic3r/GUI/Widgets/Label.hpp @@ -35,6 +35,7 @@ public: static wxFont Head_14; static wxFont Head_13; static wxFont Head_12; + static wxFont Head_11; static wxFont Head_10; static wxFont Body_16; diff --git a/src/slic3r/GUI/Widgets/WebView.cpp b/src/slic3r/GUI/Widgets/WebView.cpp index 59b1a866ac..74efaa67ba 100644 --- a/src/slic3r/GUI/Widgets/WebView.cpp +++ b/src/slic3r/GUI/Widgets/WebView.cpp @@ -6,6 +6,8 @@ #include #if wxUSE_WEBVIEW_EDGE #include +#elif defined(__WXMAC__) +#include #endif #include #if defined(__WIN32__) || defined(__WXMAC__) @@ -75,6 +77,16 @@ private: wxString pendingUserAgent; }; +#elif defined __WXOSX__ + +class WebViewWebKit : public wxWebViewWebKit +{ + ~WebViewWebKit() override + { + RemoveScriptMessageHandler("wx"); + } +}; + #endif class FakeWebView : public wxWebView @@ -116,6 +128,7 @@ class FakeWebView : public wxWebView wxDEFINE_EVENT(EVT_WEBVIEW_RECREATED, wxCommandEvent); static std::vector g_webviews; +static std::vector g_delay_webviews; class WebViewRef : public wxObjectRefData { @@ -152,6 +165,8 @@ wxWebView* WebView::CreateWebView(wxWindow * parent, wxString const & url) #ifdef __WIN32__ wxWebView* webView = new WebViewEdge; +#elif defined(__WXOSX__) + wxWebView *webView = new WebViewWebKit; #else auto webView = wxWebView::New(); #endif @@ -179,15 +194,27 @@ wxWebView* WebView::CreateWebView(wxWindow * parent, wxString const & url) WKWebView * wkWebView = (WKWebView *) webView->GetNativeBackend(); Slic3r::GUI::WKWebView_setTransparentBackground(wkWebView); #endif + auto addScriptMessageHandler = [] (wxWebView *webView) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": begin to add script message handler for wx."; + Slic3r::GUI::wxGetApp().set_adding_script_handler(true); + if (!webView->AddScriptMessageHandler("wx")) + wxLogError("Could not add script message handler"); + Slic3r::GUI::wxGetApp().set_adding_script_handler(false); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": finished add script message handler for wx."; + }; #ifndef __WIN32__ - Slic3r::GUI::wxGetApp().CallAfter([webView] { + webView->CallAfter([webView, addScriptMessageHandler] { #endif - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": begin to add script message handler for wx."; - Slic3r::GUI::wxGetApp().set_adding_script_handler(true); - if (!webView->AddScriptMessageHandler("wx")) - wxLogError("Could not add script message handler"); - Slic3r::GUI::wxGetApp().set_adding_script_handler(false); - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": finished add script message handler for wx."; + if (Slic3r::GUI::wxGetApp().is_adding_script_handler()) { + g_delay_webviews.push_back(webView); + } else { + addScriptMessageHandler(webView); + while (!g_delay_webviews.empty()) { + auto views = std::move(g_delay_webviews); + for (auto wv : views) + addScriptMessageHandler(wv); + } + } #ifndef __WIN32__ }); #endif diff --git a/src/slic3r/GUI/WipeTowerDialog.cpp b/src/slic3r/GUI/WipeTowerDialog.cpp index 776b8611bf..f79e33f264 100644 --- a/src/slic3r/GUI/WipeTowerDialog.cpp +++ b/src/slic3r/GUI/WipeTowerDialog.cpp @@ -613,8 +613,14 @@ std::vector WipingPanel::read_matrix_values() { for (unsigned int i=0;iGetValue().ToDouble(&val); - output.push_back((float)val / get_flush_multiplier()); + float flush_multipler = get_flush_multiplier(); + if (flush_multipler == 0) { + output.push_back(0.); + } + else { + edit_boxes[j][i]->GetValue().ToDouble(&val); + output.push_back((float) val / get_flush_multiplier()); + } } } return output; diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index d93c2840a6..b17a58ef4a 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -446,7 +446,7 @@ wxBitmap create_scaled_bitmap( const std::string& bmp_name_in, #ifdef _WIN32 menu_bitmap ? Slic3r::GUI::check_dark_mode() : #endif - Slic3r::GUI::wxGetApp().dark_mode(); + Slic3r::GUI::wxGetApp().dark_mode(); // Try loading an SVG first, then PNG if SVG is not found: wxBitmap *bmp = cache.load_svg(bmp_name, width, height, grayscale, dark_mode, new_color, resize ? em_unit(win) * 0.1f : 0.f); diff --git a/src/slic3r/GUI/wxMediaCtrl2.cpp b/src/slic3r/GUI/wxMediaCtrl2.cpp index f6d6855daa..3450a38454 100644 --- a/src/slic3r/GUI/wxMediaCtrl2.cpp +++ b/src/slic3r/GUI/wxMediaCtrl2.cpp @@ -31,6 +31,9 @@ wxMediaCtrl2::wxMediaCtrl2(wxWindow *parent) #ifdef __LINUX__ /* Register only after we have created the wxMediaCtrl, since only then are we guaranteed to have fired up Gstreamer's plugin registry. */ gstbambusrc_register(); + Bind(wxEVT_MEDIA_LOADED, [this](auto & e) { + m_loaded = true; + }); #endif } @@ -40,7 +43,7 @@ void wxMediaCtrl2::Load(wxURI url) { #ifdef __WIN32__ if (m_imp == nullptr) { - Slic3r::GUI::wxGetApp().CallAfter([] { + CallAfter([] { auto res = wxMessageBox(_L("Windows Media Player is required for this task! Do you want to enable 'Windows Media Player' for your operation system?"), _L("Error"), wxOK | wxCANCEL); if (res == wxOK) { wxString url = IsWindows10OrGreater() @@ -70,7 +73,7 @@ void wxMediaCtrl2::Load(wxURI url) boost::filesystem::path data_dir_path(data_dir_str); auto dll_path = data_dir_path / "plugins" / "BambuSource.dll"; if (boost::filesystem::exists(dll_path)) { - Slic3r::GUI::wxGetApp().CallAfter( + CallAfter( [dll_path] { int res = wxMessageBox(_L("BambuSource has not correctly been registered for media playing! Press Yes to re-register it."), _L("Error"), wxYES_NO); if (res == wxYES) { @@ -80,7 +83,7 @@ void wxMediaCtrl2::Load(wxURI url) } }); } else { - Slic3r::GUI::wxGetApp().CallAfter([] { + CallAfter([] { wxMessageBox(_L("Missing BambuSource component registered for media playing! Please re-install BambuStutio or seek after-sales help."), _L("Error"), wxOK); }); } @@ -129,8 +132,8 @@ void wxMediaCtrl2::Load(wxURI url) } if (!hasplugins) { - Slic3r::GUI::wxGetApp().CallAfter([] { - wxMessageBox(_L("Your system is missing H.264 codecs for GStreamer, which are required to play video. (Try installing the gstreamer1.0-plugins-bad or gstreamer1.0-libav packages, then restart Orca Slicer?)"), _L("Error"), wxOK); + CallAfter([] { + wxMessageBox(_L("Your system is missing H.264 codecs for GStreamer, which are required to play video. (Try installing the gstreamer1.0-plugins-bad or gstreamer1.0-libav packages, then restart Bambu Studio?)"), _L("Error"), wxOK); }); m_error = 101; wxMediaEvent event(wxEVT_MEDIA_STATECHANGED); @@ -139,14 +142,43 @@ void wxMediaCtrl2::Load(wxURI url) wxPostEvent(this, event); return; } + wxLog::EnableLogging(false); #endif m_error = 0; + m_loaded = false; wxMediaCtrl::Load(url); + +#ifdef __WXGTK3__ + wxMediaEvent event(wxEVT_MEDIA_STATECHANGED); + event.SetId(GetId()); + event.SetEventObject(this); + wxPostEvent(this, event); +#endif } void wxMediaCtrl2::Play() { wxMediaCtrl::Play(); } -void wxMediaCtrl2::Stop() { wxMediaCtrl::Stop(); } +void wxMediaCtrl2::Stop() +{ +#ifdef __WIN32__ + wxMediaCtrl::Load(wxURI()); +#else + wxMediaCtrl::Stop(); +#endif +} + +#ifdef __LINUX__ +extern int gst_bambu_last_error; +#endif + +int wxMediaCtrl2::GetLastError() const +{ +#ifdef __LINUX__ + return gst_bambu_last_error; +#else + return m_error; +#endif +} wxSize wxMediaCtrl2::GetVideoSize() const { @@ -154,7 +186,7 @@ wxSize wxMediaCtrl2::GetVideoSize() const // Gstreamer doesn't give us a VideoSize until we're playing, which // confuses the MediaPlayCtrl into claiming that it is stuck // "Loading...". Fake it out for now. - return wxSize(1280, 720); + return m_loaded ? wxSize(1280, 720) : wxSize{}; #else return m_imp ? m_imp->GetVideoSize() : wxSize(0, 0); #endif diff --git a/src/slic3r/GUI/wxMediaCtrl2.h b/src/slic3r/GUI/wxMediaCtrl2.h index e894793afb..3f1467a13f 100644 --- a/src/slic3r/GUI/wxMediaCtrl2.h +++ b/src/slic3r/GUI/wxMediaCtrl2.h @@ -65,7 +65,7 @@ public: void SetIdleImage(wxString const & image); - int GetLastError() const { return m_error; } + int GetLastError() const; wxSize GetVideoSize() const; @@ -83,6 +83,7 @@ protected: private: wxString m_idle_image; int m_error = 0; + bool m_loaded = false; }; #endif diff --git a/src/slic3r/Utils/MacDarkMode.mm b/src/slic3r/Utils/MacDarkMode.mm index 10248fd0fa..e539053c1d 100644 --- a/src/slic3r/Utils/MacDarkMode.mm +++ b/src/slic3r/Utils/MacDarkMode.mm @@ -208,7 +208,7 @@ void openFolderForFile(wxString const & file) @end -/* edit column for wxTableView */ +/* edit column for wxCocoaOutlineView */ #include #include @@ -216,8 +216,20 @@ void openFolderForFile(wxString const & file) @implementation wxCocoaOutlineView (Edit) +bool addObserver = false; + - (BOOL)outlineView: (NSOutlineView*) view shouldEditTableColumn:(nullable NSTableColumn *)tableColumn item:(nonnull id)item { + NSClipView * clipView = [[self enclosingScrollView] contentView]; + if (!addObserver) { + addObserver = true; + clipView.postsBoundsChangedNotifications = YES; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(synchronizedViewContentBoundsDidChange:) + name:NSViewBoundsDidChangeNotification + object:clipView]; + } + wxDataViewColumn* const col((wxDataViewColumn *)[tableColumn getColumnPointer]); wxDataViewItem item2([static_cast(item) pointer]); @@ -227,9 +239,18 @@ void openFolderForFile(wxString const & file) dvc->GetEventHandler()->ProcessEvent( event ); if( !event.IsAllowed() ) return NO; + return YES; } +- (void)synchronizedViewContentBoundsDidChange:(NSNotification *)notification +{ + wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); + wxDataViewCustomRenderer * r = dvc->GetCustomRendererPtr(); + if (r) + r->FinishEditing(); +} + @end /* Font for wxTextCtrl */ diff --git a/src/slic3r/Utils/NetworkAgent.cpp b/src/slic3r/Utils/NetworkAgent.cpp index cd63fb4156..2f4323bbfa 100644 --- a/src/slic3r/Utils/NetworkAgent.cpp +++ b/src/slic3r/Utils/NetworkAgent.cpp @@ -87,6 +87,7 @@ func_get_user_print_info NetworkAgent::get_user_print_info_ptr = null func_get_printer_firmware NetworkAgent::get_printer_firmware_ptr = nullptr; func_get_task_plate_index NetworkAgent::get_task_plate_index_ptr = nullptr; func_get_user_info NetworkAgent::get_user_info_ptr = nullptr; +func_request_bind_ticket NetworkAgent::request_bind_ticket_ptr = nullptr; func_get_slice_info NetworkAgent::get_slice_info_ptr = nullptr; func_query_bind_status NetworkAgent::query_bind_status_ptr = nullptr; func_modify_printer_name NetworkAgent::modify_printer_name_ptr = nullptr; @@ -96,6 +97,10 @@ func_get_profile_3mf NetworkAgent::get_profile_3mf_ptr = nullptr; func_get_model_publish_url NetworkAgent::get_model_publish_url_ptr = nullptr; func_get_model_mall_home_url NetworkAgent::get_model_mall_home_url_ptr = nullptr; func_get_my_profile NetworkAgent::get_my_profile_ptr = nullptr; +func_track_enable NetworkAgent::track_enable_ptr = nullptr; +func_track_event NetworkAgent::track_event_ptr = nullptr; +func_track_header NetworkAgent::track_header_ptr = nullptr; +func_track_update_property NetworkAgent::track_update_property_ptr = nullptr; NetworkAgent::NetworkAgent() @@ -229,6 +234,7 @@ int NetworkAgent::initialize_network_module(bool using_backup) get_printer_firmware_ptr = reinterpret_cast(get_network_function("bambu_network_get_printer_firmware")); get_task_plate_index_ptr = reinterpret_cast(get_network_function("bambu_network_get_task_plate_index")); get_user_info_ptr = reinterpret_cast(get_network_function("bambu_network_get_user_info")); + request_bind_ticket_ptr = reinterpret_cast(get_network_function("bambu_network_request_bind_ticket")); get_slice_info_ptr = reinterpret_cast(get_network_function("bambu_network_get_slice_info")); query_bind_status_ptr = reinterpret_cast(get_network_function("bambu_network_query_bind_status")); modify_printer_name_ptr = reinterpret_cast(get_network_function("bambu_network_modify_printer_name")); @@ -238,6 +244,10 @@ int NetworkAgent::initialize_network_module(bool using_backup) get_model_publish_url_ptr = reinterpret_cast(get_network_function("bambu_network_get_model_publish_url")); get_model_mall_home_url_ptr = reinterpret_cast(get_network_function("bambu_network_get_model_mall_home_url")); get_my_profile_ptr = reinterpret_cast(get_network_function("bambu_network_get_my_profile")); + track_enable_ptr = reinterpret_cast(get_network_function("bambu_network_track_enable")); + track_event_ptr = reinterpret_cast(get_network_function("bambu_network_track_event")); + track_header_ptr = reinterpret_cast(get_network_function("bambu_network_track_header")); + track_update_property_ptr = reinterpret_cast(get_network_function("bambu_network_track_update_property")); return 0; } @@ -334,6 +344,10 @@ int NetworkAgent::unload_network_module() get_model_publish_url_ptr = nullptr; get_model_mall_home_url_ptr = nullptr; get_my_profile_ptr = nullptr; + track_enable_ptr = nullptr; + track_event_ptr = nullptr; + track_header_ptr = nullptr; + track_update_property_ptr = nullptr; return 0; } @@ -785,11 +799,11 @@ std::string NetworkAgent::build_login_info() return ret; } -int NetworkAgent::bind(std::string dev_ip, std::string timezone, OnUpdateStatusFn update_fn) +int NetworkAgent::bind(std::string dev_ip, std::string dev_id, std::string sec_link, std::string timezone, OnUpdateStatusFn update_fn) { int ret = 0; if (network_agent && bind_ptr) { - ret = bind_ptr(network_agent, dev_ip, timezone, update_fn); + ret = bind_ptr(network_agent, dev_ip, dev_id, sec_link, timezone, update_fn); if (ret) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(" error: network_agent=%1%, ret=%2%, dev_ip=%3%, timezone=%4%") %network_agent %ret %dev_ip %timezone; @@ -1019,6 +1033,17 @@ int NetworkAgent::get_user_info(int* identifier) return ret; } +int NetworkAgent::request_bind_ticket(std::string* ticket) +{ + int ret = 0; + if (network_agent && request_bind_ticket_ptr) { + ret = request_bind_ticket_ptr(network_agent, ticket); + if (ret) + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(" error: network_agent=%1%, ret=%2%") % network_agent % ret; + } + return ret; +} + int NetworkAgent::get_slice_info(std::string project_id, std::string profile_id, int plate_index, std::string* slice_json) { int ret; @@ -1117,4 +1142,52 @@ int NetworkAgent::get_my_profile(std::string token, unsigned int *http_code, std return ret; } +int NetworkAgent::track_enable(bool enable) +{ + enable_track = enable; + int ret = 0; + if (network_agent && track_enable_ptr) { + ret = track_enable_ptr(network_agent, enable); + if (ret) + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format("error network_agnet=%1%, ret = %2%") % network_agent % ret; + } + return ret; +} + +int NetworkAgent::track_event(std::string evt_key, std::string content) +{ + if (!this->enable_track) + return 0; + + int ret = 0; + if (network_agent && track_event_ptr) { + ret = track_event_ptr(network_agent, evt_key, content); + if (ret) + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format("error network_agnet=%1%, ret = %2%") % network_agent % ret; + } + return ret; +} + +int NetworkAgent::track_header(std::string header) +{ + int ret = 0; + if (network_agent && track_header_ptr) { + ret = track_header_ptr(network_agent, header); + if (ret) + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format("error network_agnet=%1%, ret = %2%") % network_agent % ret; + } + return ret; +} + +int NetworkAgent::track_update_property(std::string name, std::string value, std::string type) +{ + int ret = 0; + if (network_agent && track_update_property_ptr) { + ret = track_update_property_ptr(network_agent, name, value, type); + if (ret) + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format("error network_agnet=%1%, ret = %2%") % network_agent % ret; + } + return ret; +} + } //namespace diff --git a/src/slic3r/Utils/NetworkAgent.hpp b/src/slic3r/Utils/NetworkAgent.hpp index 7d8b593214..cf1cea3a2a 100644 --- a/src/slic3r/Utils/NetworkAgent.hpp +++ b/src/slic3r/Utils/NetworkAgent.hpp @@ -45,7 +45,7 @@ typedef std::string (*func_get_user_nickanme)(void *agent); typedef std::string (*func_build_login_cmd)(void *agent); typedef std::string (*func_build_logout_cmd)(void *agent); typedef std::string (*func_build_login_info)(void *agent); -typedef int (*func_bind)(void *agent, std::string dev_ip, std::string timezone, OnUpdateStatusFn update_fn); +typedef int (*func_bind)(void *agent, std::string dev_ip, std::string dev_id, std::string sec_link, std::string timezone, OnUpdateStatusFn update_fn); typedef int (*func_unbind)(void *agent, std::string dev_id); typedef std::string (*func_get_bambulab_host)(void *agent); typedef std::string (*func_get_user_selected_machine)(void *agent); @@ -67,6 +67,7 @@ typedef int (*func_get_user_print_info)(void *agent, unsigned int* http_code, st typedef int (*func_get_printer_firmware)(void *agent, std::string dev_id, unsigned* http_code, std::string* http_body); typedef int (*func_get_task_plate_index)(void *agent, std::string task_id, int* plate_index); typedef int (*func_get_user_info)(void *agent, int* identifier); +typedef int (*func_request_bind_ticket)(void *agent, std::string* ticket); typedef int (*func_get_slice_info)(void *agent, std::string project_id, std::string profile_id, int plate_index, std::string* slice_json); typedef int (*func_query_bind_status)(void *agent, std::vector query_list, unsigned int* http_code, std::string* http_body); typedef int (*func_modify_printer_name)(void *agent, std::string dev_id, std::string dev_name); @@ -76,6 +77,10 @@ typedef int (*func_get_profile_3mf)(void *agent, BBLProfile* profile); typedef int (*func_get_model_publish_url)(void *agent, std::string* url); typedef int (*func_get_model_mall_home_url)(void *agent, std::string* url); typedef int (*func_get_my_profile)(void *agent, std::string token, unsigned int *http_code, std::string *http_body); +typedef int (*func_track_enable)(void *agent, bool enable); +typedef int (*func_track_event)(void *agent, std::string evt_key, std::string content); +typedef int (*func_track_header)(void *agent, std::string header); +typedef int (*func_track_update_property)(void *agent, std::string name, std::string value, std::string type); //the NetworkAgent class @@ -129,7 +134,7 @@ public: std::string build_login_cmd(); std::string build_logout_cmd(); std::string build_login_info(); - int bind(std::string dev_ip, std::string timezone, OnUpdateStatusFn update_fn); + int bind(std::string dev_ip, std::string dev_id, std::string sec_link, std::string timezone, OnUpdateStatusFn update_fn); int unbind(std::string dev_id); std::string get_bambulab_host(); std::string get_user_selected_machine(); @@ -151,6 +156,7 @@ public: int get_printer_firmware(std::string dev_id, unsigned* http_code, std::string* http_body); int get_task_plate_index(std::string task_id, int* plate_index); int get_user_info(int* identifier); + int request_bind_ticket(std::string* ticket); int get_slice_info(std::string project_id, std::string profile_id, int plate_index, std::string* slice_json); int query_bind_status(std::vector query_list, unsigned int* http_code, std::string* http_body); int modify_printer_name(std::string dev_id, std::string dev_name); @@ -160,9 +166,12 @@ public: int get_model_publish_url(std::string* url); int get_model_mall_home_url(std::string* url); int get_my_profile(std::string token, unsigned int* http_code, std::string* http_body); - + int track_enable(bool enable); + int track_event(std::string evt_key, std::string content); + int track_header(std::string header); + int track_update_property(std::string name, std::string value, std::string type = "string"); private: - + bool enable_track = false; void* network_agent { nullptr }; static func_check_debug_consistent check_debug_consistent_ptr; @@ -225,6 +234,7 @@ private: static func_get_printer_firmware get_printer_firmware_ptr; static func_get_task_plate_index get_task_plate_index_ptr; static func_get_user_info get_user_info_ptr; + static func_request_bind_ticket request_bind_ticket_ptr; static func_get_slice_info get_slice_info_ptr; static func_query_bind_status query_bind_status_ptr; static func_modify_printer_name modify_printer_name_ptr; @@ -234,6 +244,10 @@ private: static func_get_model_publish_url get_model_publish_url_ptr; static func_get_model_mall_home_url get_model_mall_home_url_ptr; static func_get_my_profile get_my_profile_ptr; + static func_track_enable track_enable_ptr; + static func_track_event track_event_ptr; + static func_track_header track_header_ptr; + static func_track_update_property track_update_property_ptr; }; } diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp index 715065adab..6771be97d7 100644 --- a/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -881,8 +881,79 @@ void PresetUpdater::priv::sync_plugins(std::string http_url, std::string plugin_ std::string cached_version; get_cached_plugins_version(cached_version); - if (!cached_version.empty()) - plugin_version = cached_version; + if (!cached_version.empty()) { + bool need_delete_cache = false; + Semver current_semver = curr_version; + Semver cached_semver = cached_version; + + int curent_patch_cc = current_semver.patch()/100; + int cached_patch_cc = cached_semver.patch()/100; + int curent_patch_dd = current_semver.patch()%100; + int cached_patch_dd = cached_semver.patch()%100; + if ((cached_semver.maj() != current_semver.maj()) + || (cached_semver.min() != current_semver.min()) + || (curent_patch_cc != cached_patch_cc)) + { + need_delete_cache = true; + BOOST_LOG_TRIVIAL(info) << boost::format("cached plugins version %1% not match with current %2%")%cached_version%curr_version; + } + else if (cached_patch_dd <= curent_patch_dd) { + need_delete_cache = true; + BOOST_LOG_TRIVIAL(info) << boost::format("cached plugins version %1% not newer than current %2%")%cached_version%curr_version; + } + else { + plugin_version = cached_version; + } + + if (need_delete_cache) { + std::string data_dir_str = data_dir(); + boost::filesystem::path data_dir_path(data_dir_str); + auto cache_folder = data_dir_path / "ota"; + +#if defined(_MSC_VER) || defined(_WIN32) + auto network_library = cache_folder / "bambu_networking.dll"; + auto player_library = cache_folder / "BambuSource.dll"; +#elif defined(__WXMAC__) + auto network_library = cache_folder / "libbambu_networking.dylib"; + auto player_library = cache_folder / "libBambuSource.dylib"; +#else + auto network_library = cache_folder / "libbambu_networking.so"; + auto player_library = cache_folder / "libBambuSource.so"; +#endif + auto changelog_file = cache_folder / "network_plugins.json"; + + if (boost::filesystem::exists(network_library)) + { + + BOOST_LOG_TRIVIAL(info) << "[remove_old_networking_plugins] remove the file "< resources @@ -1173,7 +1244,7 @@ void PresetUpdater::sync(std::string http_url, std::string language, std::string // Copy the whole vendors data for use in the background thread // Unfortunatelly as of C++11, it needs to be copied again // into the closure (but perhaps the compiler can elide this). - VendorMap vendors = preset_bundle->vendors; + VendorMap vendors = preset_bundle ? preset_bundle->vendors : VendorMap{}; p->thread = std::thread([this, vendors, http_url, language, plugin_version]() { this->p->prune_tmps(); @@ -1182,7 +1253,8 @@ void PresetUpdater::sync(std::string http_url, std::string language, std::string this->p->sync_version(); if (p->cancel) return; - this->p->sync_config(http_url, std::move(vendors)); + if (!vendors.empty()) + this->p->sync_config(http_url, std::move(vendors)); if (p->cancel) return; this->p->sync_plugins(http_url, plugin_version); diff --git a/src/slic3r/Utils/bambu_networking.hpp b/src/slic3r/Utils/bambu_networking.hpp index 6ae7ad8827..8e46f88c72 100644 --- a/src/slic3r/Utils/bambu_networking.hpp +++ b/src/slic3r/Utils/bambu_networking.hpp @@ -41,7 +41,8 @@ namespace BBL { #define BAMBU_NETWORK_LIBRARY "bambu_networking" #define BAMBU_NETWORK_AGENT_NAME "bambu_network_agent" -#define BAMBU_NETWORK_AGENT_VERSION "01.05.00.08" +#define BAMBU_NETWORK_AGENT_VERSION "01.06.00.01" + //iot preset type strings #define IOT_PRINTER_TYPE_STRING "printer" @@ -143,6 +144,8 @@ struct PrintParams { bool task_layer_inspect; /* first layer inspection of task */ bool task_record_timelapse; /* record timelapse of task */ bool task_use_ams; + std::string task_bed_type; + std::string extra_options; }; struct PublishParams { diff --git a/version.inc b/version.inc index 088fdb9371..be040f6598 100644 --- a/version.inc +++ b/version.inc @@ -11,4 +11,4 @@ if(NOT DEFINED BBL_INTERNAL_TESTING) set(BBL_INTERNAL_TESTING "1") endif() set(SoftFever_VERSION "1.6.2-beta") -set(SLIC3R_VERSION "01.05.00.61") +set(SLIC3R_VERSION "01.06.00.65") \ No newline at end of file