Compare commits

..
Author SHA1 Message Date
SoftFever db33aaa4dc Extend Prusa's tip forming to the MK4 MMU3 and CORE One INDX 2026-10-06 18:25:06 +08:00
SoftFever cd959d5d87 Match PrusaSlicer's tip forming on CORE One filaments
Ramming, load/unload, cooling-move and stamping values follow the
PrusaSlicer 3.0 presets, which retune several of them relative to
2.9.6. The CORE One MMU3 0.6 nozzle now uses the 0.6 Generic PLA and
Prusament rPLA profiles, as in PrusaSlicer.
2026-10-06 18:02:32 +08:00
91 changed files with 770 additions and 738 deletions
+7 -2
View File
@@ -36,8 +36,13 @@ override is not available in these profiles. The source's `EXCLUDE_E_START` and
`EXCLUDE_E_END` internal markers become comments rather than printer commands.
Pressure-advance restoration and automatic pressure-advance emission use the
selected filament preset's settings. These profiles do not impose machine-owned
filament overrides. Dock-fan control retains the source's material and layer
selected filament preset's settings. The INDX filament presets include
`fdm_filament_template_indx`, whose start G-code sets the filament's pressure
advance with `M572` and then starts the firmware's automatic calibration with
`M573 R`, the commands PrusaSlicer emits after a tool change; the station purge
disables pressure advance before it. The template also carries Prusa's
multi-tool ramming and a 10 mm³ minimal purge. These profiles do not impose
machine-owned filament overrides. Dock-fan control retains the source's material and layer
conditions; shutdown parks the tool and turns off the used heaters and dock fan.
## Configuration boundaries
+97 -37
View File
@@ -1,6 +1,6 @@
{
"name": "Prusa",
"version": "02.04.00.15",
"version": "02.04.00.16",
"force_update": "0",
"description": "Prusa configurations",
"machine_model_list": [
@@ -1573,48 +1573,32 @@
],
"filament_list": [
{
"name": "Generic ASA @Prusa CORE One",
"sub_path": "filament/Generic ASA @Prusa CORE One.json"
},
{
"name": "Generic PLA Silk @Prusa CORE One",
"sub_path": "filament/Generic PLA Silk @Prusa CORE One.json"
"name": "Generic PC @Prusa",
"sub_path": "filament/Generic PC @Prusa.json"
},
{
"name": "fdm_filament_common",
"sub_path": "filament/fdm_filament_common.json"
},
{
"name": "Generic ASA @Prusa CORE One 0.6",
"sub_path": "filament/Generic ASA @Prusa CORE One 0.6.json"
"name": "fdm_filament_template_indx",
"sub_path": "filament/fdm_filament_template_indx.json"
},
{
"name": "Generic ASA @Prusa CORE One 0.8",
"sub_path": "filament/Generic ASA @Prusa CORE One 0.8.json"
"name": "fdm_filament_template_nextruder_pet",
"sub_path": "filament/fdm_filament_template_nextruder_pet.json"
},
{
"name": "Generic ASA @Prusa CORE One HF 0.4",
"sub_path": "filament/Generic ASA @Prusa CORE One HF 0.4.json"
"name": "fdm_filament_template_nextruder_pet_hf",
"sub_path": "filament/fdm_filament_template_nextruder_pet_hf.json"
},
{
"name": "Generic ASA @Prusa CORE One HF 0.5",
"sub_path": "filament/Generic ASA @Prusa CORE One HF 0.5.json"
"name": "fdm_filament_template_nextruder_pla",
"sub_path": "filament/fdm_filament_template_nextruder_pla.json"
},
{
"name": "Generic ASA @Prusa CORE One HF 0.6",
"sub_path": "filament/Generic ASA @Prusa CORE One HF 0.6.json"
},
{
"name": "Generic ASA @Prusa CORE One HF 0.8",
"sub_path": "filament/Generic ASA @Prusa CORE One HF 0.8.json"
},
{
"name": "Generic PLA Silk @Prusa CORE One 0.6",
"sub_path": "filament/Generic PLA Silk @Prusa CORE One 0.6.json"
},
{
"name": "Generic PLA Silk @Prusa CORE One 0.8",
"sub_path": "filament/Generic PLA Silk @Prusa CORE One 0.8.json"
"name": "fdm_filament_template_nextruder_pla_hf",
"sub_path": "filament/fdm_filament_template_nextruder_pla_hf.json"
},
{
"name": "fdm_filament_abs",
@@ -1664,6 +1648,22 @@
"name": "fdm_filament_tpu",
"sub_path": "filament/fdm_filament_tpu.json"
},
{
"name": "Generic PLA Silk @Prusa CORE One INDX",
"sub_path": "filament/Generic PLA Silk @Prusa CORE One INDX.json"
},
{
"name": "Generic ASA @Prusa CORE One",
"sub_path": "filament/Generic ASA @Prusa CORE One.json"
},
{
"name": "Generic PC @Prusa MK4 MMU3",
"sub_path": "filament/Generic PC @Prusa MK4 MMU3.json"
},
{
"name": "Generic PLA Silk @Prusa CORE One",
"sub_path": "filament/Generic PLA Silk @Prusa CORE One.json"
},
{
"name": "Generic ABS @Prusa MK3.5",
"sub_path": "filament/Generic ABS @Prusa MK3.5.json"
@@ -2160,6 +2160,38 @@
"name": "Generic TPU HF @Prusa MK3.5",
"sub_path": "filament/Generic TPU HF @Prusa MK3.5.json"
},
{
"name": "Generic ASA @Prusa CORE One 0.6",
"sub_path": "filament/Generic ASA @Prusa CORE One 0.6.json"
},
{
"name": "Generic ASA @Prusa CORE One 0.8",
"sub_path": "filament/Generic ASA @Prusa CORE One 0.8.json"
},
{
"name": "Generic ASA @Prusa CORE One HF 0.4",
"sub_path": "filament/Generic ASA @Prusa CORE One HF 0.4.json"
},
{
"name": "Generic ASA @Prusa CORE One HF 0.5",
"sub_path": "filament/Generic ASA @Prusa CORE One HF 0.5.json"
},
{
"name": "Generic ASA @Prusa CORE One HF 0.6",
"sub_path": "filament/Generic ASA @Prusa CORE One HF 0.6.json"
},
{
"name": "Generic ASA @Prusa CORE One HF 0.8",
"sub_path": "filament/Generic ASA @Prusa CORE One HF 0.8.json"
},
{
"name": "Generic PLA Silk @Prusa CORE One 0.6",
"sub_path": "filament/Generic PLA Silk @Prusa CORE One 0.6.json"
},
{
"name": "Generic PLA Silk @Prusa CORE One 0.8",
"sub_path": "filament/Generic PLA Silk @Prusa CORE One 0.8.json"
},
{
"name": "Generic ABS @Prusa MK4S HF0.4",
"sub_path": "filament/Generic ABS @Prusa MK4S HF0.4.json"
@@ -2212,6 +2244,10 @@
"name": "Generic ABS HF @Prusa MINIIS 0.8",
"sub_path": "filament/Generic ABS HF @Prusa MINIIS 0.8.json"
},
{
"name": "Generic ASA @Prusa MK4 MMU3",
"sub_path": "filament/Generic ASA @Prusa MK4 MMU3.json"
},
{
"name": "Generic ASA @Prusa MK4S 0.6",
"sub_path": "filament/Generic ASA @Prusa MK4S 0.6.json"
@@ -2260,6 +2296,10 @@
"name": "Prusament ASA @CORE One HF 0.8",
"sub_path": "filament/Prusament ASA @CORE One HF 0.8.json"
},
{
"name": "Prusament ASA @MK4 MMU3",
"sub_path": "filament/Prusament ASA @MK4 MMU3.json"
},
{
"name": "Prusament ASA @MK4S 0.6",
"sub_path": "filament/Prusament ASA @MK4S 0.6.json"
@@ -2392,6 +2432,10 @@
"name": "Prusament PC Blend @CORE One HF 0.8",
"sub_path": "filament/Prusament PC Blend @CORE One HF 0.8.json"
},
{
"name": "Prusament PC Blend @MK4 MMU3",
"sub_path": "filament/Prusament PC Blend @MK4 MMU3.json"
},
{
"name": "Prusament PC Blend @MK4S 0.6",
"sub_path": "filament/Prusament PC Blend @MK4S 0.6.json"
@@ -2532,6 +2576,10 @@
"name": "Prusament PETG @CORE One HF 0.8",
"sub_path": "filament/Prusament PETG @CORE One HF 0.8.json"
},
{
"name": "Prusament PETG @MK4 MMU3",
"sub_path": "filament/Prusament PETG @MK4 MMU3.json"
},
{
"name": "Prusament PETG @MK4S 0.6",
"sub_path": "filament/Prusament PETG @MK4S 0.6.json"
@@ -2668,6 +2716,10 @@
"name": "Prusament PLA @CORE One HF 0.8",
"sub_path": "filament/Prusament PLA @CORE One HF 0.8.json"
},
{
"name": "Prusament PLA @MK4 MMU3",
"sub_path": "filament/Prusament PLA @MK4 MMU3.json"
},
{
"name": "Prusament PLA @MK4S 0.6",
"sub_path": "filament/Prusament PLA @MK4S 0.6.json"
@@ -2704,6 +2756,10 @@
"name": "Prusament rPLA @CORE One 0.8",
"sub_path": "filament/Prusament rPLA @CORE One 0.8.json"
},
{
"name": "Prusament rPLA @MK4 MMU3",
"sub_path": "filament/Prusament rPLA @MK4 MMU3.json"
},
{
"name": "Prusament rPLA @MK4S 0.6",
"sub_path": "filament/Prusament rPLA @MK4S 0.6.json"
@@ -2856,6 +2912,10 @@
"name": "Generic ABS @Prusa CORE One HF 0.8",
"sub_path": "filament/Generic ABS @Prusa CORE One HF 0.8.json"
},
{
"name": "Generic ABS @Prusa MK4 MMU3",
"sub_path": "filament/Generic ABS @Prusa MK4 MMU3.json"
},
{
"name": "Generic ASA @Prusa MK4S HF0.5",
"sub_path": "filament/Generic ASA @Prusa MK4S HF0.5.json"
@@ -2904,6 +2964,10 @@
"name": "Generic PETG @Prusa CORE One HF 0.8",
"sub_path": "filament/Generic PETG @Prusa CORE One HF 0.8.json"
},
{
"name": "Generic PETG @Prusa MK4 MMU3",
"sub_path": "filament/Generic PETG @Prusa MK4 MMU3.json"
},
{
"name": "Generic PLA @Prusa MK4S HF0.5",
"sub_path": "filament/Generic PLA @Prusa MK4S HF0.5.json"
@@ -2948,6 +3012,10 @@
"name": "Generic PLA @Prusa CORE One HF 0.8",
"sub_path": "filament/Generic PLA @Prusa CORE One HF 0.8.json"
},
{
"name": "Generic PLA @Prusa MK4 MMU3",
"sub_path": "filament/Generic PLA @Prusa MK4 MMU3.json"
},
{
"name": "Generic TPU @Prusa CORE One 0.6",
"sub_path": "filament/Generic TPU @Prusa CORE One 0.6.json"
@@ -2955,14 +3023,6 @@
{
"name": "Generic TPU @Prusa CORE One 0.8",
"sub_path": "filament/Generic TPU @Prusa CORE One 0.8.json"
},
{
"name": "Generic PC @Prusa",
"sub_path": "filament/Generic PC @Prusa.json"
},
{
"name": "Generic PLA Silk @Prusa CORE One INDX",
"sub_path": "filament/Generic PLA Silk @Prusa CORE One INDX.json"
}
],
"machine_list": [
@@ -7,6 +7,9 @@
"setting_id": "ASYz8yASE82tQDII",
"filament_id": "OFY9muEs",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet_hf"
],
"compatible_printers": [
"Prusa CORE One HF 0.4 nozzle"
],
@@ -7,6 +7,9 @@
"setting_id": "mzZCm8XDmAR23YR6",
"filament_id": "OFY9muEs",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet_hf"
],
"compatible_printers": [
"Prusa CORE One HF 0.5 nozzle"
],
@@ -7,6 +7,9 @@
"setting_id": "gJhX2KBpk4wfXIj0",
"filament_id": "OFY9muEs",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet_hf"
],
"compatible_printers": [
"Prusa CORE One HF 0.6 nozzle"
],
@@ -5,6 +5,9 @@
"from": "system",
"setting_id": "JEatvybG9xIkrPk3",
"instantiation": "true",
"include": [
"fdm_filament_template_indx"
],
"filament_cost": [
"34.65"
],
@@ -6,6 +6,9 @@
"from": "system",
"setting_id": "0k31oXq1pLQsHvrj",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet"
],
"chamber_temperature": [
"40"
],
@@ -0,0 +1,19 @@
{
"type": "filament",
"name": "Generic ABS @Prusa MK4 MMU3",
"inherits": "Generic ABS @Prusa MK4",
"from": "system",
"setting_id": "uFmixo2FX1yxNsDB",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet"
],
"compatible_printers": [
"Prusa MK4 MMU3 0.4 nozzle",
"Prusa MK4 MMU3 0.6 nozzle",
"Prusa MK4 MMU3 HF0.4 nozzle"
],
"filament_minimal_purge_on_wipe_tower": [
"35"
]
}
@@ -7,6 +7,9 @@
"setting_id": "eKDfiVRE59itoppm",
"filament_id": "OFLPAxz3",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet_hf"
],
"compatible_printers": [
"Prusa CORE One HF 0.4 nozzle"
],
@@ -7,6 +7,9 @@
"setting_id": "ZjHf5Vvtf4ESNnWb",
"filament_id": "OFLPAxz3",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet_hf"
],
"compatible_printers": [
"Prusa CORE One HF 0.5 nozzle"
],
@@ -7,6 +7,9 @@
"setting_id": "aRDgUgQJWdNDJx5c",
"filament_id": "OFLPAxz3",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet_hf"
],
"compatible_printers": [
"Prusa CORE One HF 0.6 nozzle"
],
@@ -6,6 +6,9 @@
"from": "system",
"setting_id": "Q7fgojkHRZI0floy",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet"
],
"chamber_temperature": [
"40"
],
@@ -0,0 +1,19 @@
{
"type": "filament",
"name": "Generic ASA @Prusa MK4 MMU3",
"inherits": "Generic ASA @Prusa MK4",
"from": "system",
"setting_id": "iq9bFkpmAOJ4ae4g",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet"
],
"compatible_printers": [
"Prusa MK4 MMU3 0.4 nozzle",
"Prusa MK4 MMU3 0.6 nozzle",
"Prusa MK4 MMU3 HF0.4 nozzle"
],
"filament_minimal_purge_on_wipe_tower": [
"35"
]
}
@@ -0,0 +1,16 @@
{
"type": "filament",
"name": "Generic PC @Prusa MK4 MMU3",
"inherits": "Generic PC @Prusa",
"from": "system",
"setting_id": "qMawlK9QPI7DcF8I",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet"
],
"compatible_printers": [
"Prusa MK4 MMU3 0.4 nozzle",
"Prusa MK4 MMU3 0.6 nozzle",
"Prusa MK4 MMU3 HF0.4 nozzle"
]
}
@@ -7,6 +7,9 @@
"setting_id": "7oUyjvRHLH4bJyX0",
"filament_id": "OFYPdQJh",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet_hf"
],
"compatible_printers": [
"Prusa CORE One HF 0.4 nozzle"
],
@@ -7,6 +7,9 @@
"setting_id": "6HiIOGURZWwman1l",
"filament_id": "OFYPdQJh",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet_hf"
],
"compatible_printers": [
"Prusa CORE One HF 0.5 nozzle"
],
@@ -7,6 +7,9 @@
"setting_id": "KKiug7A9NOQPe1m7",
"filament_id": "OFYPdQJh",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet_hf"
],
"compatible_printers": [
"Prusa CORE One HF 0.6 nozzle"
],
@@ -5,6 +5,9 @@
"from": "system",
"setting_id": "HbevqxqvYHuvRc3v",
"instantiation": "true",
"include": [
"fdm_filament_template_indx"
],
"filament_cost": [
"27.82"
],
@@ -6,6 +6,9 @@
"from": "system",
"setting_id": "W71PW5bpf7CV16D4",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet"
],
"chamber_temperature": [
"35"
],
@@ -0,0 +1,19 @@
{
"type": "filament",
"name": "Generic PETG @Prusa MK4 MMU3",
"inherits": "Generic PETG @Prusa MK4",
"from": "system",
"setting_id": "lBiZBFpSXZNIp5XG",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet"
],
"compatible_printers": [
"Prusa MK4 MMU3 0.4 nozzle",
"Prusa MK4 MMU3 0.6 nozzle",
"Prusa MK4 MMU3 HF0.4 nozzle"
],
"filament_minimal_purge_on_wipe_tower": [
"35"
]
}
@@ -8,12 +8,16 @@
"filament_id": "OFDSrzZ8",
"instantiation": "true",
"compatible_printers": [
"Prusa CORE One 0.6 nozzle"
"Prusa CORE One 0.6 nozzle",
"Prusa CORE One MMU3 0.6 nozzle"
],
"nozzle_temperature": [
"210"
],
"slow_down_layer_time": [
"15"
],
"filament_cooling_final_speed": [
"1.5"
]
}
@@ -7,6 +7,9 @@
"setting_id": "vniEPaoyABoAfLFa",
"filament_id": "OFDSrzZ8",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pla_hf"
],
"compatible_printers": [
"Prusa CORE One HF 0.4 nozzle",
"Prusa CORE One L HF 0.4 nozzle"
@@ -7,6 +7,9 @@
"setting_id": "NPEs4gtBlKWupYZX",
"filament_id": "OFDSrzZ8",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pla_hf"
],
"compatible_printers": [
"Prusa CORE One HF 0.5 nozzle"
],
@@ -7,6 +7,9 @@
"setting_id": "SnfULjrWGCiA7ljq",
"filament_id": "OFDSrzZ8",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pla_hf"
],
"compatible_printers": [
"Prusa CORE One HF 0.6 nozzle"
],
@@ -24,5 +27,8 @@
],
"slow_down_min_speed": [
"15"
],
"filament_cooling_final_speed": [
"1.5"
]
}
@@ -5,6 +5,9 @@
"from": "system",
"setting_id": "4qjcaIf1vk3K1x4Z",
"instantiation": "true",
"include": [
"fdm_filament_template_indx"
],
"filament_cost": [
"25.4"
],
@@ -6,6 +6,9 @@
"from": "system",
"setting_id": "r2i4c7DsYXYIMVCX",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pla"
],
"chamber_temperature": [
"20"
],
@@ -17,8 +20,7 @@
"Prusa CORE One L 0.4 nozzle",
"Prusa CORE One MMU3 0.4 nozzle",
"Prusa CORE One MMU3 0.25 nozzle",
"Prusa CORE One MMU3 0.3 nozzle",
"Prusa CORE One MMU3 0.6 nozzle"
"Prusa CORE One MMU3 0.3 nozzle"
],
"fan_cooling_layer_time": [
"40"
@@ -0,0 +1,16 @@
{
"type": "filament",
"name": "Generic PLA @Prusa MK4 MMU3",
"inherits": "Generic PLA @Prusa MK4",
"from": "system",
"setting_id": "kZvIh84niJ487FsY",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pla"
],
"compatible_printers": [
"Prusa MK4 MMU3 0.4 nozzle",
"Prusa MK4 MMU3 0.6 nozzle",
"Prusa MK4 MMU3 HF0.4 nozzle"
]
}
@@ -29,5 +29,8 @@
],
"slow_down_layer_time": [
"15"
],
"filament_cooling_final_speed": [
"1.5"
]
}
@@ -5,6 +5,9 @@
"from": "system",
"setting_id": "eKla6su22rYaL9iz",
"instantiation": "true",
"include": [
"fdm_filament_template_indx"
],
"filament_flow_ratio": [
"1",
"1",
@@ -6,6 +6,9 @@
"from": "system",
"setting_id": "F9lNHyhZwZi7aD74",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pla"
],
"chamber_temperature": [
"20"
],
@@ -6,6 +6,9 @@
"setting_id": "tXktQXr79am1s5UL",
"filament_id": "OFmFwUWM",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet_hf"
],
"compatible_printers": [
"Prusa CORE One HF 0.4 nozzle"
],
@@ -6,6 +6,9 @@
"setting_id": "YbyojtjXvEtI4Tt3",
"filament_id": "OFmFwUWM",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet_hf"
],
"compatible_printers": [
"Prusa CORE One HF 0.5 nozzle"
],
@@ -6,6 +6,9 @@
"setting_id": "35H9TLr1p2Jy767g",
"filament_id": "OFmFwUWM",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet_hf"
],
"compatible_printers": [
"Prusa CORE One HF 0.6 nozzle"
],
@@ -7,6 +7,9 @@
"setting_id": "UUTjJ4Y8St3c87jD",
"filament_id": "OFmFwUWM",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet"
],
"chamber_temperature": [
"40"
],
@@ -0,0 +1,19 @@
{
"type": "filament",
"name": "Prusament ASA @MK4 MMU3",
"inherits": "Prusament ASA @MK4",
"from": "system",
"setting_id": "IRJ2dtBgjCMfmpOH",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet"
],
"compatible_printers": [
"Prusa MK4 MMU3 0.4 nozzle",
"Prusa MK4 MMU3 0.6 nozzle",
"Prusa MK4 MMU3 HF0.4 nozzle"
],
"filament_minimal_purge_on_wipe_tower": [
"35"
]
}
@@ -7,6 +7,9 @@
"setting_id": "sNGgucQjiFXwuFkq",
"filament_id": "OF4MZtnE",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet"
],
"chamber_temperature": [
"40"
],
@@ -6,6 +6,9 @@
"setting_id": "pEsP537TBR5kozQB",
"filament_id": "OFvDbkFq",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet_hf"
],
"compatible_printers": [
"Prusa CORE One HF 0.4 nozzle"
],
@@ -6,6 +6,9 @@
"setting_id": "3IfLi1ZVCKKbEBUW",
"filament_id": "OFvDbkFq",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet_hf"
],
"compatible_printers": [
"Prusa CORE One HF 0.5 nozzle"
],
@@ -6,6 +6,9 @@
"setting_id": "Q8qWw2G7l2UvxGut",
"filament_id": "OFvDbkFq",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet_hf"
],
"compatible_printers": [
"Prusa CORE One HF 0.6 nozzle"
],
@@ -7,6 +7,9 @@
"setting_id": "x1SkWhSSkVVIPwCF",
"filament_id": "OFvDbkFq",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet"
],
"chamber_temperature": [
"40"
],
@@ -0,0 +1,16 @@
{
"type": "filament",
"name": "Prusament PC Blend @MK4 MMU3",
"inherits": "Prusament PC Blend @MK4",
"from": "system",
"setting_id": "gmNuCspHHxgP6zH8",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet"
],
"compatible_printers": [
"Prusa MK4 MMU3 0.4 nozzle",
"Prusa MK4 MMU3 0.6 nozzle",
"Prusa MK4 MMU3 HF0.4 nozzle"
]
}
@@ -7,6 +7,9 @@
"setting_id": "B8Cm9l8i9osp5J2q",
"filament_id": "OFdO6vpt",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet"
],
"chamber_temperature": [
"40"
],
@@ -6,6 +6,9 @@
"setting_id": "lwT1VxW5OkY36kUX",
"filament_id": "OFkR8E2c",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet_hf"
],
"compatible_printers": [
"Prusa CORE One HF 0.4 nozzle"
],
@@ -6,6 +6,9 @@
"setting_id": "XAgW7fElXxhaKaBT",
"filament_id": "OFkR8E2c",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet_hf"
],
"compatible_printers": [
"Prusa CORE One HF 0.5 nozzle"
],
@@ -6,6 +6,9 @@
"setting_id": "6aTGEZj2EmpTR1mu",
"filament_id": "OFkR8E2c",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet_hf"
],
"compatible_printers": [
"Prusa CORE One HF 0.6 nozzle"
],
@@ -7,6 +7,9 @@
"setting_id": "LSvba9ds3XTwDMCG",
"filament_id": "OFkR8E2c",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet"
],
"chamber_temperature": [
"35"
],
@@ -0,0 +1,19 @@
{
"type": "filament",
"name": "Prusament PETG @MK4 MMU3",
"inherits": "Prusament PETG @MK4",
"from": "system",
"setting_id": "7DQ6YEKyQKYDTKtE",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pet"
],
"compatible_printers": [
"Prusa MK4 MMU3 0.4 nozzle",
"Prusa MK4 MMU3 0.6 nozzle",
"Prusa MK4 MMU3 HF0.4 nozzle"
],
"filament_minimal_purge_on_wipe_tower": [
"35"
]
}
@@ -6,6 +6,9 @@
"setting_id": "2viuOmBvXuQm2FwV",
"filament_id": "OFkR8E2c",
"instantiation": "true",
"include": [
"fdm_filament_template_indx"
],
"filament_flow_ratio": [
"1"
],
@@ -6,6 +6,9 @@
"setting_id": "mf4Muog4IcsgkYoA",
"filament_id": "OFnO4wnf",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pla_hf"
],
"compatible_printers": [
"Prusa CORE One HF 0.4 nozzle",
"Prusa CORE One L HF 0.4 nozzle"
@@ -7,6 +7,9 @@
"setting_id": "PuujUMjNKLZvZNAH",
"filament_id": "OFnO4wnf",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pla"
],
"chamber_temperature": [
"20"
],
@@ -104,5 +107,11 @@
],
"slow_down_min_speed": [
"20"
],
"filament_unloading_speed": [
"100"
],
"filament_unloading_speed_start": [
"100"
]
}
@@ -0,0 +1,22 @@
{
"type": "filament",
"name": "Prusament PLA @MK4 MMU3",
"inherits": "Prusament PLA @MK4",
"from": "system",
"setting_id": "MsDcN4npnmLRXOjm",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pla"
],
"compatible_printers": [
"Prusa MK4 MMU3 0.4 nozzle",
"Prusa MK4 MMU3 0.6 nozzle",
"Prusa MK4 MMU3 HF0.4 nozzle"
],
"filament_unloading_speed": [
"100"
],
"filament_unloading_speed_start": [
"100"
]
}
@@ -6,6 +6,9 @@
"setting_id": "B2UB9QUbbjpn12Ka",
"filament_id": "OFnO4wnf",
"instantiation": "true",
"include": [
"fdm_filament_template_indx"
],
"filament_flow_ratio": [
"1"
],
@@ -7,6 +7,9 @@
"setting_id": "085dGkrnPhatF07x",
"filament_id": "OFh7mvPO",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pla"
],
"chamber_temperature": [
"20"
],
@@ -105,5 +108,11 @@
],
"slow_down_min_speed": [
"20"
],
"filament_unloading_speed": [
"60"
],
"filament_unloading_speed_start": [
"60"
]
}
@@ -8,12 +8,16 @@
"instantiation": "true",
"compatible_printers": [
"Prusa CORE One 0.6 nozzle",
"Prusa CORE One HF 0.6 nozzle"
"Prusa CORE One HF 0.6 nozzle",
"Prusa CORE One MMU3 0.6 nozzle"
],
"filament_max_volumetric_speed": [
"14"
],
"slow_down_layer_time": [
"15"
],
"filament_cooling_final_speed": [
"1.5"
]
}
@@ -7,6 +7,9 @@
"setting_id": "Bw6H4vFnq7YExyLd",
"filament_id": "OFWRITFw",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pla"
],
"chamber_temperature": [
"20"
],
@@ -22,8 +25,7 @@
"Prusa CORE One HF 0.5 nozzle",
"Prusa CORE One MMU3 0.4 nozzle",
"Prusa CORE One MMU3 0.25 nozzle",
"Prusa CORE One MMU3 0.3 nozzle",
"Prusa CORE One MMU3 0.6 nozzle"
"Prusa CORE One MMU3 0.3 nozzle"
],
"fan_cooling_layer_time": [
"40"
@@ -0,0 +1,16 @@
{
"type": "filament",
"name": "Prusament rPLA @MK4 MMU3",
"inherits": "Prusament rPLA @MK4",
"from": "system",
"setting_id": "wKhvyGpgjmW6glLj",
"instantiation": "true",
"include": [
"fdm_filament_template_nextruder_pla"
],
"compatible_printers": [
"Prusa MK4 MMU3 0.4 nozzle",
"Prusa MK4 MMU3 0.6 nozzle",
"Prusa MK4 MMU3 HF0.4 nozzle"
]
}
@@ -0,0 +1,21 @@
{
"type": "filament",
"name": "fdm_filament_template_indx",
"from": "system",
"instantiation": "false",
"filament_multitool_ramming": [
"1"
],
"filament_multitool_ramming_flow": [
"30"
],
"filament_multitool_ramming_volume": [
"18"
],
"filament_minimal_purge_on_wipe_tower": [
"10"
],
"filament_start_gcode": [
"M572 S{pressure_advance[filament_extruder_id]} ; Pressure advance\nM573 R"
]
}
@@ -0,0 +1,36 @@
{
"type": "filament",
"name": "fdm_filament_template_nextruder_pet",
"from": "system",
"instantiation": "false",
"filament_ramming_parameters": [
"250 100 42.4194 42.4194 42.4194 42.4194 42.4194| 0.05 42.4387 0.45 42.4387 0.95 42.4387 1.45 42.4387 1.95 42.4387 2.45 42.4387 2.95 42.4387 3.45 42.4387 3.95 42.4387 4.45 42.4387 4.95 42.4387"
],
"filament_loading_speed": [
"10"
],
"filament_loading_speed_start": [
"50"
],
"filament_unloading_speed": [
"60"
],
"filament_unloading_speed_start": [
"60"
],
"filament_cooling_moves": [
"3"
],
"filament_cooling_initial_speed": [
"5"
],
"filament_cooling_final_speed": [
"2.5"
],
"filament_stamping_distance": [
"45"
],
"filament_stamping_loading_speed": [
"26.5"
]
}
@@ -0,0 +1,9 @@
{
"type": "filament",
"name": "fdm_filament_template_nextruder_pet_hf",
"from": "system",
"instantiation": "false",
"filament_ramming_parameters": [
"250 100 42.4194 42.4194| 0.05 42.4387 0.45 42.4387 0.95 42.4387 1.45 42.4387 1.95 42.4387 2.45 42.4387 2.95 42.4387 3.45 42.4387 3.95 42.4387 4.45 42.4387 4.95 42.4387"
]
}
@@ -0,0 +1,36 @@
{
"type": "filament",
"name": "fdm_filament_template_nextruder_pla",
"from": "system",
"instantiation": "false",
"filament_ramming_parameters": [
"250 100 40.1613 40.3548 40.4516 40.3548 40.2581| 0.05 40.1483 0.45 40.3419 0.95 40.3419 1.45 40.3419 1.95 40.3419 2.45 40.3419 2.95 40.3419 3.45 40.3419 3.95 40.3419 4.45 40.3419 4.95 40.3419"
],
"filament_loading_speed": [
"10"
],
"filament_loading_speed_start": [
"50"
],
"filament_unloading_speed": [
"40"
],
"filament_unloading_speed_start": [
"30"
],
"filament_cooling_moves": [
"2"
],
"filament_cooling_initial_speed": [
"10"
],
"filament_cooling_final_speed": [
"3.5"
],
"filament_stamping_distance": [
"45"
],
"filament_stamping_loading_speed": [
"29"
]
}
@@ -0,0 +1,9 @@
{
"type": "filament",
"name": "fdm_filament_template_nextruder_pla_hf",
"from": "system",
"instantiation": "false",
"filament_ramming_parameters": [
"250 100 39.871 39.871| 0.05 39.9547 0.45 39.9547 0.95 39.9547 1.45 39.9547 1.95 39.9547 2.45 39.9547 2.95 39.9547 3.45 39.9547 3.95 39.9547 4.45 39.9547 4.95 39.9547"
]
}
@@ -3,7 +3,7 @@
"name": "Prusa CORE One MMU3",
"bed_model": "coreone_bed.stl",
"bed_texture": "coreone.svg",
"default_materials": "Generic ABS @Prusa CORE One;Generic ASA @Prusa CORE One;Generic PETG @Prusa CORE One;Generic PLA @Prusa CORE One;Generic PLA Silk @Prusa CORE One;Generic TPU @Prusa CORE One",
"default_materials": "Generic ABS @Prusa CORE One;Generic ASA @Prusa CORE One;Generic PETG @Prusa CORE One;Generic PLA @Prusa CORE One;Generic PLA Silk @Prusa CORE One;Generic TPU @Prusa CORE One;Generic PLA @Prusa CORE One 0.6",
"family": "Prusa",
"hotend_model": "",
"machine_tech": "FFF",
@@ -16,7 +16,7 @@
],
"printer_model": "Prusa MK4 MMU3",
"default_filament_profile": [
"Generic PLA @System"
"Generic PLA @Prusa MK4 MMU3"
],
"default_print_profile": "0.20mm SPEED @MK4 MMU3 0.4",
"printer_notes": "Don't remove the following keywords! These keywords are used in the \"compatible printer\" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_MODEL_MK4IS\nPRINTER_MODEL_MK4MMU3\nPG"
@@ -17,7 +17,7 @@
"printer_model": "Prusa MK4 MMU3",
"printer_variant": "0.6",
"default_filament_profile": [
"Generic PLA @System"
"Generic PLA @Prusa MK4 MMU3"
],
"default_print_profile": "0.25mm SPEED @MK4 MMU3 0.6",
"printer_notes": "Don't remove the following keywords! These keywords are used in the \"compatible printer\" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_MODEL_MK4IS\nPRINTER_MODEL_MK4MMU3\nPG"
@@ -8,5 +8,5 @@
"bed_model": "mk4_bed.stl",
"bed_texture": "mk4is.svg",
"hotend_model": "",
"default_materials": "Generic PLA @System;Generic PETG @System;Generic ABS @System"
"default_materials": "Generic PLA @Prusa MK4 MMU3;Generic PETG @Prusa MK4 MMU3;Generic ABS @Prusa MK4 MMU3"
}
@@ -16,7 +16,7 @@
],
"printer_model": "Prusa MK4 MMU3 HF",
"default_filament_profile": [
"Generic PLA @System"
"Generic PLA @Prusa MK4 MMU3"
],
"default_print_profile": "0.20mm SPEED @MK4 MMU3 HF0.4",
"printer_notes": "Don't remove the following keywords! These keywords are used in the \"compatible printer\" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_MODEL_MK4IS\nPRINTER_MODEL_MK4MMU3\nPG\nHF_NOZZLE"
@@ -8,5 +8,5 @@
"bed_model": "mk4_bed.stl",
"bed_texture": "mk4is.svg",
"hotend_model": "",
"default_materials": "Generic PLA @System;Generic PETG @System;Generic ABS @System"
"default_materials": "Generic PLA @Prusa MK4 MMU3;Generic PETG @Prusa MK4 MMU3;Generic ABS @Prusa MK4 MMU3"
}
+14 -45
View File
@@ -8,9 +8,6 @@ include) fails wherever the error is. Headers are compiled on their own, and
fail only on errors in their changed lines. Deleting an #include also fails
on every use, changed or not, that now lacks the header it provided.
With -- --fix, clang-tidy adds the missing includes, on those lines only, and
each file it changed is checked again so that only what remains is reported.
The checks come from .clang-tidy at the repository root. The compile database
must come from a configure with SLIC3R_PCH=OFF, or the precompiled header hides
missing includes.
@@ -105,10 +102,9 @@ def is_checked(path):
def changed_files(merge_base):
# Against the working tree, so a local run covers uncommitted edits too.
# core.quotePath=false keeps a non-ASCII path unquoted, and the explicit prefixes
# override diff.noprefix and diff.mnemonicPrefix, so parse_diff sees its b/ prefix.
# core.quotePath=false keeps a non-ASCII path unquoted, so parse_diff sees its b/ prefix.
diff = subprocess.run(["git", "-c", "core.quotePath=false", "diff", "-U0", "--no-color", "--no-ext-diff",
"--src-prefix=a/", "--dst-prefix=b/", "--diff-filter=AMR", merge_base],
"--diff-filter=AMR", merge_base],
check=True, capture_output=True, **UTF8).stdout
return {path: change for path, change in parse_diff(diff).items() if is_checked(path)}
@@ -205,19 +201,13 @@ def errors_alone_at(revision, clang_tidy, build_dir, path):
def check_file(clang_tidy, build_dir, merge_base, path, change, extra_args):
"""Run clang-tidy on one file and return (failed, output, failing diagnostics, fixed)."""
fixing = any(arg.startswith("--fix") for arg in extra_args)
if fixing:
with open(path, "rb") as f:
before = f.read()
"""Run clang-tidy on one file and return (failed, output, failing diagnostics)."""
# A deleted include can orphan uses on unchanged lines, so such a file is
# checked whole and the findings narrowed here. --fix keeps a line filter so
# it never rewrites unrelated code, which for such a file means a second,
# fixing run limited to the lines the first one found wanting.
whole = bool(change.removed_includes)
# checked whole and the findings narrowed here. --fix keeps the line filter
# so it never rewrites unrelated code.
whole = bool(change.removed_includes) and not extra_args
returncode, output, diagnostics = run_clang_tidy(clang_tidy, build_dir, path,
None if whole else change.lines,
[] if whole else extra_args)
None if whole else change.lines, extra_args)
real = os.path.realpath(path)
def introduced(d):
@@ -233,25 +223,14 @@ def check_file(clang_tidy, build_dir, merge_base, path, change, extra_args):
if errors and change.removed_includes:
with open(path, encoding="utf-8") as f:
text = f.read()
before_sites = errors_alone_at(merge_base, clang_tidy, build_dir, path)
before = errors_alone_at(merge_base, clang_tidy, build_dir, path)
failing += [d for d in errors if d not in failing
and (error_sites([d], text) - before_sites)]
failed = bool(failing)
elif whole:
and (error_sites([d], text) - before)]
return bool(failing), output, failing
if whole:
failing = [d for d in diagnostics if d.is_compile_error or introduced(d)]
failed = bool(failing)
else:
failing, failed = diagnostics, returncode != 0
if not fixing:
return failed, output, failing, False
if whole and failing:
lines = change.lines + [[d.line, d.line] for d in failing if os.path.realpath(d.file) == real]
run_clang_tidy(clang_tidy, build_dir, path, lines, extra_args)
with open(path, "rb") as f:
if f.read() == before:
return failed, output, failing, False
# Checked again, so what is reported is what the fixes left.
return check_file(clang_tidy, build_dir, merge_base, path, change, [])[:3] + (True,)
return bool(failing), output, failing
return returncode != 0, output, diagnostics
def main():
@@ -284,14 +263,11 @@ def main():
annotate = os.environ.get("GITHUB_ACTIONS") == "true"
root = os.getcwd() + os.sep
failed = []
fixed = []
with ThreadPoolExecutor(max_workers=args.jobs) as pool:
jobs = {path: pool.submit(check_file, args.clang_tidy, args.build_dir, merge_base, path, change, args.extra_args)
for path, change in todo}
for path, job in jobs.items():
file_failed, output, diagnostics, file_fixed = job.result()
if file_fixed:
fixed.append(path)
file_failed, output, diagnostics = job.result()
if not file_failed:
continue
failed.append(path)
@@ -306,13 +282,6 @@ def main():
if len(diagnostics) > MAX_REPORTED:
print(f"... and {len(diagnostics) - MAX_REPORTED} more")
if fixed:
print(f"\nAdded includes to {len(fixed)} file(s):")
for path in fixed:
print(f" {path}")
if failed and fixed:
print(f"\nclang-tidy still fails on {len(failed)} file(s); the findings above are what --fix could not add.")
return 1
if failed:
print(f"\nclang-tidy failed on {len(failed)} file(s). Add the includes it names, or apply its "
"suggestions locally with scripts/run_clang_tidy.sh --fix (scripts\\run_clang_tidy.ps1 -Fix on Windows).")
+10 -40
View File
@@ -6,9 +6,8 @@
# scripts/run_clang_tidy.sh --fix also add the missing includes it names
#
# It configures a separate build directory (build-tidy) without the precompiled
# header, uses the clang-tidy on your system or installs the pinned one into a
# virtual environment inside it, and runs scripts/clang_tidy_diff.py the way CI
# does. Uncommitted changes are checked too.
# header, installs the pinned clang-tidy into a virtual environment inside it, and
# runs scripts/clang_tidy_diff.py the way CI does. Uncommitted changes are checked too.
set -euo pipefail
@@ -24,8 +23,7 @@ Usage: scripts/run_clang_tidy.sh [options]
deps/build/<arch> on macOS)
-j, --jobs N parallel clang-tidy runs (default: all cores)
--fix apply clang-tidy's fixes (adds the missing includes)
-y, --yes install missing tools without asking; another clang-tidy
version found on the system is then not offered
-y, --yes install missing tools without asking
-h, --help show this help
EOF
}
@@ -138,38 +136,12 @@ REQUIREMENTS="$ROOT/scripts/clang_tidy_requirements.txt"
PINNED=$(sed -n 's/^clang-tidy==//p' "$REQUIREMENTS")
VENV="$BUILD_DIR/clang-tidy-venv"
is_pinned() {
[ -x "$1" ] && "$1" --version 2>/dev/null | grep -q "version $PINNED"
}
CLANG_TIDY="${CLANG_TIDY:-}"
if [ -n "$CLANG_TIDY" ]; then
if [ -n "${CLANG_TIDY:-}" ]; then
# Set by the caller: use it as is.
is_pinned "$CLANG_TIDY" || echo "Warning: $CLANG_TIDY is not clang-tidy $PINNED, so results may differ from CI." >&2
:
else
# One already on the system comes first: the pinned version outright, another
# version if the user accepts the difference. The pinned version is installed
# into a virtual environment otherwise.
INSTALLED=""
for candidate in $(command -v clang-tidy "clang-tidy-${PINNED%%.*}" || true) \
"/usr/lib/llvm-${PINNED%%.*}/bin/clang-tidy" \
"$(brew --prefix llvm 2>/dev/null || true)/bin/clang-tidy"; do
if is_pinned "$candidate"; then
CLANG_TIDY="$candidate"
break
fi
[ -z "$INSTALLED" ] && [ -x "$candidate" ] && INSTALLED="$candidate"
done
if [ -z "$CLANG_TIDY" ] && [ -n "$INSTALLED" ] && [ "$YES" = 0 ] && ! is_pinned "$VENV/bin/clang-tidy"; then
echo "Found $INSTALLED, which is $("$INSTALLED" --version | sed -n 's/.*version \([0-9.]*\).*/\1/p' | head -n 1), not the $PINNED CI uses, so results may differ slightly."
if ask "Use it anyway?"; then
CLANG_TIDY="$INSTALLED"
fi
fi
fi
if [ -z "$CLANG_TIDY" ]; then
CLANG_TIDY="$VENV/bin/clang-tidy"
if ! is_pinned "$CLANG_TIDY"; then
if [ ! -x "$CLANG_TIDY" ] || ! "$CLANG_TIDY" --version | grep -q "version $PINNED"; then
if ask "clang-tidy $PINNED (the version CI uses) is not installed. Install it into $VENV?"; then
mkdir -p "$BUILD_DIR"
if ! python3 -m venv "$VENV"; then
@@ -186,6 +158,9 @@ if [ -z "$CLANG_TIDY" ]; then
fi
fi
fi
if ! "$CLANG_TIDY" --version | grep -q "version $PINNED"; then
echo "Warning: $CLANG_TIDY is not clang-tidy $PINNED, so results may differ from CI." >&2
fi
# --- Dependencies -------------------------------------------------------------
@@ -242,12 +217,7 @@ cmake --build "$BUILD_DIR" --target git_commit_hash_header >/dev/null
if [ -z "$BASE" ]; then
REMOTE=$(git remote -v | awk '/github\.com[:\/]OrcaSlicer\/OrcaSlicer(\.git)? \(fetch\)/ { print $1; exit }')
if [ -z "$REMOTE" ]; then
# Against a fork's main that already has the commits, nothing is checked.
echo "Warning: no remote points at github.com/OrcaSlicer/OrcaSlicer, so this compares against origin/main." >&2
echo "If origin is your fork, add the upstream remote (git remote add upstream https://github.com/OrcaSlicer/OrcaSlicer.git) or pass --base." >&2
REMOTE=origin
fi
REMOTE="${REMOTE:-origin}"
if [ "$FETCH" = 1 ]; then
git fetch --quiet "$REMOTE" main
fi
-57
View File
@@ -155,8 +155,6 @@ class TestSubprocessCalls(unittest.TestCase):
diff = "+++ b/src/libslic3r/Über.cpp\n@@ -1,0 +2 @@\n+// 打印\n"
files, call, _ = self.run_patched(clang_tidy_diff.changed_files, "base", stdout=diff)
self.assertIn("core.quotePath=false", call.args[0])
# Whatever diff.noprefix or diff.mnemonicPrefix a user has set.
self.assertIn("--dst-prefix=b/", call.args[0])
self.assertEqual(call.kwargs["encoding"], "utf-8")
self.assertEqual(files["src/libslic3r/Über.cpp"].lines, [[2, 2]])
@@ -169,60 +167,5 @@ class TestSubprocessCalls(unittest.TestCase):
self.assertEqual(call.kwargs["encoding"], "utf-8")
class TestCheckFileFix(unittest.TestCase):
"""check_file with -- --fix: what clang-tidy is run on, and what is reported afterwards."""
def setUp(self):
self.dir = tempfile.TemporaryDirectory()
self.addCleanup(self.dir.cleanup)
self.path = os.path.join(self.dir.name, "Color.cpp")
with open(self.path, "w") as f:
f.write("int x;\n")
def finding(self, line, include=""):
return clang_tidy_diff.Diagnostic(self.path, line, 1, "error",
'no header providing "x" is directly included [misc-include-cleaner]', include)
def check(self, change, results, fix_writes=None):
"""Run check_file with run_clang_tidy answering from `results` in turn; the --fix run
rewrites the file with `fix_writes` when given. Returns (result, calls)."""
calls = []
def run(clang_tidy, build_dir, path, lines, extra_args):
calls.append((lines, extra_args))
if "--fix" in extra_args and fix_writes is not None:
with open(path, "w") as f:
f.write(fix_writes)
return results[len(calls) - 1]
with mock.patch.object(clang_tidy_diff, "run_clang_tidy", side_effect=run):
result = clang_tidy_diff.check_file("clang-tidy", "build", "base", self.path, change, ["--fix"])
return result, calls
def test_a_deleted_include_is_fixed_on_the_lines_it_orphaned_only(self):
change = clang_tidy_diff.FileChange(lines=[[4, 4]], removed_includes={"libslic3r/Point.hpp"})
orphaned = self.finding(50, "<libslic3r/Point.hpp>")
unrelated = self.finding(60, "<vector>")
(failed, _, failing, fixed), calls = self.check(
change, [(1, "", [orphaned, unrelated]), (1, "", [orphaned]), (0, "", [])], fix_writes="#include <libslic3r/Point.hpp>\n")
self.assertEqual(calls, [(None, []), ([[4, 4], [50, 50]], ["--fix"]), (None, [])])
self.assertEqual((failed, failing, fixed), (False, [], True))
def test_a_file_the_fix_did_not_change_keeps_its_findings(self):
change = clang_tidy_diff.FileChange(lines=[[4, 4]])
error = clang_tidy_diff.Diagnostic(self.path, 4, 1, "error", "unknown type name 'Foo' [clang-diagnostic-error]")
(failed, _, failing, fixed), calls = self.check(change, [(1, "", [error])])
self.assertEqual(calls, [([[4, 4]], ["--fix"])])
self.assertEqual((failed, failing, fixed), (True, [error], False))
def test_a_changed_file_is_checked_again_and_reports_what_is_left(self):
change = clang_tidy_diff.FileChange(lines=[[4, 4]])
error = clang_tidy_diff.Diagnostic(self.path, 4, 1, "error", "unknown type name 'Foo' [clang-diagnostic-error]")
(failed, _, failing, fixed), calls = self.check(
change, [(1, "", [self.finding(4, "<vector>"), error]), (1, "", [error])], fix_writes="#include <vector>\n")
self.assertEqual(calls, [([[4, 4]], ["--fix"]), ([[4, 4]], [])])
self.assertEqual((failed, failing, fixed), (True, [error], True))
if __name__ == "__main__":
unittest.main()
+105 -83
View File
@@ -751,50 +751,6 @@ template<typename T>
}
}
namespace {
// Writes G-code to a file in blocks and records in lines_ends the file offset after every '\n'
class GCodeFileWriter
{
public:
GCodeFileWriter(FilePtr& out, const std::string& out_path, std::vector<size_t>& lines_ends, const char* error_message)
: m_out(out), m_out_path(out_path), m_lines_ends(lines_ends), m_error_message(error_message)
{}
~GCodeFileWriter() { assert(m_buffer.empty() || std::uncaught_exceptions() > 0); }
void append(std::string_view text)
{
const size_t text_pos = m_file_pos + m_buffer.size();
for (size_t i = text.find('\n'); i != std::string_view::npos; i = text.find('\n', i + 1))
m_lines_ends.emplace_back(text_pos + i + 1);
m_buffer += text;
if (m_buffer.size() >= GCodeProcessor::Output_Block_Size)
flush();
}
void flush()
{
if (m_buffer.empty())
return;
fwrite(m_buffer.data(), 1, m_buffer.size(), m_out.f);
if (ferror(m_out.f)) {
m_out.close();
boost::nowide::remove(m_out_path.c_str());
throw Slic3r::RuntimeError(m_error_message);
}
m_file_pos += m_buffer.size();
m_buffer.clear();
}
private:
FilePtr& m_out;
const std::string& m_out_path;
std::vector<size_t>& m_lines_ends;
const char* m_error_message;
std::string m_buffer;
size_t m_file_pos{0};
};
} // namespace
// Helper class to modify and export gcode to file
class ExportLines
{
@@ -809,6 +765,16 @@ public:
enum class EWriteType { BySize, ByTime };
private:
static void update_lines_ends_and_out_file_pos(const std::string& out_string, std::vector<size_t>& lines_ends, size_t* out_file_pos)
{
for (size_t i = 0; i < out_string.size(); ++i) {
if (out_string[i] == '\n')
lines_ends.emplace_back((out_file_pos != nullptr) ? *out_file_pos + i + 1 : i + 1);
}
if (out_file_pos != nullptr)
*out_file_pos += out_string.size();
}
struct LineData
{
std::string line;
@@ -848,14 +814,12 @@ private:
EWriteType m_write_type{EWriteType::BySize};
// Time machines containing g1 times cache
const std::array<GCodeProcessor::TimeMachine, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)>& m_machines;
// Output file writer
GCodeFileWriter& m_writer;
// Current time
std::array<float, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> m_times{0.0f, 0.0f};
// Current size of the cache in bytes
// Current size in bytes
size_t m_size{0};
// gcode lines cache, used only when writing by time
// gcode lines cache
std::deque<LineData> m_lines;
size_t m_added_lines_counter{0};
// map of gcode line ids from original to final
@@ -863,16 +827,16 @@ private:
std::vector<std::pair<size_t, size_t>> m_gcode_lines_map;
size_t m_times_cache_id{0};
size_t m_out_file_pos{0};
public:
ExportLines(EWriteType type, const std::array<GCodeProcessor::TimeMachine, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)>& machines, GCodeFileWriter& writer)
ExportLines(EWriteType type, const std::array<GCodeProcessor::TimeMachine, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)>& machines)
#ifndef NDEBUG
: m_statistics(*this)
, m_write_type(type)
, m_machines(machines)
, m_writer(writer){}
, m_machines(machines){}
#else
: m_write_type(type), m_machines(machines), m_writer(writer)
: m_write_type(type), m_machines(machines)
{}
#endif // NDEBUG
@@ -926,14 +890,11 @@ public:
if (line.empty())
return;
if (m_write_type == EWriteType::ByTime) {
m_lines.push_back({line, m_times});
m_lines.push_back({line, m_times});
#ifndef NDEBUG
m_statistics.add_line(line.length());
m_statistics.add_line(line.length());
#endif // NDEBUG
m_size += line.length();
} else
m_writer.append(line);
m_size += line.length();
++m_added_lines_counter;
if (!ignore_from_move) {
assert(!m_gcode_lines_map.empty());
@@ -999,35 +960,65 @@ public:
}
}
// when writing by time, pass the cached lines older than m_times[Normal] - backtrace_time to the writer
void write(float backtrace_time)
// write to file:
// m_write_type == EWriteType::ByTime - all lines older than m_time - backtrace_time
// m_write_type == EWriteType::BySize - all lines if current size is greater than 65535 bytes
void write(FilePtr& out, float backtrace_time, GCodeProcessorResult& result, const std::string& out_path)
{
if (m_write_type != EWriteType::ByTime)
if (m_lines.empty())
return;
while (!m_lines.empty() && m_lines.front().times[Normal] < m_times[Normal] - backtrace_time) {
const LineData& data = m_lines.front();
m_writer.append(data.line);
m_size -= data.line.length();
m_lines.pop_front();
// collect lines to write into a single string
std::string out_string;
if (!m_lines.empty()) {
if (m_write_type == EWriteType::ByTime) {
while (m_lines.front().times[Normal] < m_times[Normal] - backtrace_time) {
const LineData& data = m_lines.front();
out_string += data.line;
m_size -= data.line.length();
m_lines.pop_front();
#ifndef NDEBUG
m_statistics.remove_line();
m_statistics.remove_line();
#endif // NDEBUG
}
} else {
if (m_size > 65535) {
while (!m_lines.empty()) {
out_string += m_lines.front().line;
m_lines.pop_front();
}
m_size = 0;
#ifndef NDEBUG
m_statistics.remove_all_lines();
#endif // NDEBUG
}
}
}
{
write_to_file(out, out_string, result, out_path);
update_lines_ends_and_out_file_pos(out_string, result.lines_ends, &m_out_file_pos);
}
}
// flush the current content of the cache and the writer to file
void flush()
// flush the current content of the cache to file
void flush(FilePtr& out, GCodeProcessorResult& result, const std::string& out_path)
{
// collect lines to flush into a single string
std::string out_string;
while (!m_lines.empty()) {
m_writer.append(m_lines.front().line);
out_string += m_lines.front().line;
m_lines.pop_front();
}
m_size = 0;
#ifndef NDEBUG
m_statistics.remove_all_lines();
#endif // NDEBUG
m_writer.flush();
{
write_to_file(out, out_string, result, out_path);
update_lines_ends_and_out_file_pos(out_string, result.lines_ends, &m_out_file_pos);
}
}
void synchronize_moves(GCodeProcessorResult& result) const
@@ -1060,7 +1051,20 @@ public:
size_t get_size() const { return m_size; }
void reserve(size_t lines_count) { m_gcode_lines_map.reserve(lines_count); }
private:
void write_to_file(FilePtr& out, const std::string& out_string, GCodeProcessorResult& result, const std::string& out_path)
{
if (!out_string.empty()) {
if (true) {
fwrite((const void*) out_string.c_str(), 1, out_string.length(), out.f);
if (ferror(out.f)) {
out.close();
boost::nowide::remove(out_path.c_str());
throw Slic3r::RuntimeError("GCode processor post process export failed.\nIs the disk full?");
}
}
}
}
};
void GCodeProcessor::run_post_process()
@@ -1156,13 +1160,8 @@ void GCodeProcessor::run_post_process()
last_exported_stop[i] = time_in_minutes(m_time_processor.machines[i].time);
}
m_result.lines_ends.clear();
// m_result.lines_ends.emplace_back(std::vector<size_t>());
GCodeFileWriter writer(out, out_path, m_result.lines_ends, "GCode processor post process export failed.\nIs the disk full?");
ExportLines export_line(m_result.backtrace_enabled ? ExportLines::EWriteType::ByTime : ExportLines::EWriteType::BySize,
m_time_processor.machines, writer);
// The line map holds an entry for each line of the file, and the first pass counted them
export_line.reserve(m_line_id);
m_time_processor.machines);
// replace placeholder lines with the proper final value
// gcode_line is in/out parameter, to reduce expensive memory allocation
@@ -1536,6 +1535,9 @@ void GCodeProcessor::run_post_process()
}
};
m_result.lines_ends.clear();
// m_result.lines_ends.emplace_back(std::vector<size_t>());
// Orca: freshly collect SKIPPABLE ranges each post-process pass. The ranges are stored on the
// member (rather than a local) so the injection pass can consume them, hence the clear here to
// avoid stale ranges on re-invocation.
@@ -1800,7 +1802,7 @@ void GCodeProcessor::run_post_process()
if (!gcode_line.empty())
export_line.append_line(gcode_line);
export_line.write(1.1f * max_backtrace_time);
export_line.write(out, 1.1f * max_backtrace_time, m_result, out_path);
gcode_line.clear();
}
}
@@ -1842,7 +1844,7 @@ void GCodeProcessor::run_post_process()
}
}
export_line.flush();
export_line.flush(out, m_result, out_path);
out.close();
in.close();
@@ -1990,13 +1992,31 @@ void GCodeProcessor::run_second_pass_injection()
// The rewrite may shift byte positions (once the injector inserts lines), so rebuild lines_ends from scratch.
// With an empty map the scanned '\n' offsets reproduce the current lines_ends exactly.
m_result.lines_ends.clear();
GCodeFileWriter writer(out, out_path, m_result.lines_ends, "GCode processor pre-heat injection pass failed.\nIs the disk full?\n");
size_t out_file_pos = 0;
auto write_out = [&out, &out_path, this, &out_file_pos](std::string& str) {
if (str.empty())
return;
fwrite((const void*) str.c_str(), 1, str.length(), out.f);
if (ferror(out.f)) {
out.close();
boost::nowide::remove(out_path.c_str());
throw Slic3r::RuntimeError(std::string("GCode processor pre-heat injection pass failed.\nIs the disk full?\n"));
}
for (size_t i = 0; i < str.size(); ++i) {
if (str[i] == '\n')
m_result.lines_ends.emplace_back(out_file_pos + i + 1);
}
out_file_pos += str.size();
str.clear();
};
// Orca: read/split lines with EOL-preserving semantics (keep the original \r and \n bytes, and
// synthesize no trailing newline). This is required for the empty-map identity: normalizing every
// line ending to "\n" would not be byte-identical if the finished file used \r\n or lacked a
// final newline.
std::string gcode_line;
std::string export_buffer;
unsigned int line_id = 0;
auto op_it = inserted_operation_lines.begin();
std::vector<char> buffer(65536 * 10, 0);
@@ -2038,14 +2058,16 @@ void GCodeProcessor::run_second_pass_injection()
}
++op_it;
}
writer.append(gcode_line);
export_buffer += gcode_line;
gcode_line.clear();
if (export_buffer.length() >= 65536)
write_out(export_buffer);
}
}
if (eof)
break;
}
writer.flush();
write_out(export_buffer);
out.close();
in.close();
-3
View File
@@ -491,9 +491,6 @@ class Print;
static const std::string VFlush_End_Tag;
static const std::string External_Purge_Tag;
public:
// Size of the blocks the post-processing passes write the G-code in
static constexpr size_t Output_Block_Size = 65536;
// Orca: SKIPPABLE region tags, stored as static strings (the FLUSH idiom above) rather than
// a CustomETags/CustomTags array. Public so the emission sites (WipeTower / change_filament
// path) can reference them single-sourced.
+26 -125
View File
@@ -759,59 +759,6 @@ static void clip_inner_walls_over_top(std::vector<Arachne::VariableWidthLines> &
}
}
// ORCA: only_one_wall_top - widest bead of the given walls.
static coord_t widest_bead(const std::vector<Arachne::VariableWidthLines> &walls)
{
coord_t widest = 0;
for (const Arachne::VariableWidthLines &group : walls)
for (const Arachne::ExtrusionLine &el : group)
for (const Arachne::ExtrusionJunction &j : el.junctions)
widest = std::max(widest, j.w);
return widest;
}
// ORCA: only_one_wall_top - length of the walls running further than tolerance from the reference walls, outside the
// excluded area.
static double length_off_reference(const std::vector<Arachne::VariableWidthLines> &walls, const Arachne::VariableWidthLines &reference,
const ExPolygons &excluded, coord_t tolerance)
{
auto append_centerlines = [](const Arachne::VariableWidthLines &lines, Polylines &out) {
for (const Arachne::ExtrusionLine &el : lines) {
if (el.junctions.size() < 2)
continue;
Polyline &centerline = out.emplace_back();
centerline.points.reserve(el.junctions.size());
for (const Arachne::ExtrusionJunction &j : el.junctions)
centerline.points.emplace_back(j.p);
}
};
Polylines wall_centerlines;
Polylines reference_centerlines;
for (const Arachne::VariableWidthLines &group : walls)
append_centerlines(group, wall_centerlines);
append_centerlines(reference, reference_centerlines);
Polylines off_reference = diff_pl(wall_centerlines, offset(reference_centerlines, float(tolerance)));
if (! excluded.empty())
off_reference = diff_pl(off_reference, excluded);
return total_length(off_reference);
}
// ORCA: only_one_wall_top - area covered by the given walls at their local widths.
static Polygons walls_footprint(const Arachne::VariableWidthLines &walls)
{
Polygons footprint;
for (const Arachne::ExtrusionLine &el : walls)
for (size_t i = 1; i < el.junctions.size(); ++ i) {
const Arachne::ExtrusionJunction &a = el.junctions[i - 1];
const Arachne::ExtrusionJunction &b = el.junctions[i];
const coord_t width = std::max(a.w, b.w);
if (width > 0)
append(footprint, offset(Polyline(a.p, b.p), float(width) / 2.f));
}
return union_(footprint);
}
void PerimeterGenerator::split_top_surfaces(const ExPolygons &orig_polygons, ExPolygons &top_fills,
ExPolygons &non_top_polygons, ExPolygons &fill_clip) const {
// other perimeters
@@ -2599,90 +2546,44 @@ void PerimeterGenerator::process_arachne()
if (inner_loop_number >= 0) {
assert(upper_slices != nullptr);
// Infill contour bounding box.
BoundingBox infill_contour_bbox = get_extents(infill_contour);
infill_contour_bbox.offset(SCALED_EPSILON);
coord_t perimeter_width = this->perimeter_flow.scaled_width();
// Filter out areas that are too thin and expand top surface polygons a bit to hide the wall line.
// ORCA: skip if the top surface area is smaller than "min_width_top_surface"
const float top_surface_min_width = std::max<float>(float(ext_perimeter_spacing) / 4.f + scaled<float>(0.00001), float(scale_(config->min_width_top_surface.get_abs_value(unscale_(perimeter_width)))) / 4.f);
// Get top ExPolygons from current infill contour.
Polygons upper_slices_clipped;
if (object_config->interface_shells) {
auto upper_slicer_same_region = to_expolygons(this->upper_slices_same_region->surfaces);
upper_slices_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(upper_slicer_same_region, infill_contour_bbox);
} else
upper_slices_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*upper_slices, infill_contour_bbox);
// Get top ExPolygons from the given contour. uncovered reports whether the upper layer leaves any of the
// contour uncovered, before bridges and too thin areas are filtered out.
auto get_top_expolygons = [&](const ExPolygons &contour, bool &uncovered) {
// Contour bounding box.
BoundingBox contour_bbox = get_extents(contour);
contour_bbox.offset(SCALED_EPSILON);
Polygons upper_slices_clipped;
if (object_config->interface_shells) {
auto upper_slicer_same_region = to_expolygons(this->upper_slices_same_region->surfaces);
upper_slices_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(upper_slicer_same_region, contour_bbox);
} else
upper_slices_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*upper_slices, contour_bbox);
ExPolygons top = diff_ex(contour, upper_slices_clipped);
uncovered = !top.empty();
if (top.empty())
return top;
top_expolygons = diff_ex(infill_contour, upper_slices_clipped);
if (!top_expolygons.empty()) {
if (lower_slices != nullptr) {
const float bridge_offset = float(std::max<coord_t>(ext_perimeter_spacing, perimeter_width));
const Polygons lower_slices_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*lower_slices, contour_bbox);
const ExPolygons current_slices_bridges = offset_ex(diff_ex(top, lower_slices_clipped), bridge_offset);
const Polygons lower_slices_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*lower_slices, infill_contour_bbox);
const ExPolygons current_slices_bridges = offset_ex(diff_ex(top_expolygons, lower_slices_clipped), bridge_offset);
// Remove bridges from top surface polygons.
top = diff_ex(top, current_slices_bridges);
top_expolygons = diff_ex(top_expolygons, current_slices_bridges);
}
// Filter out areas that are too thin and expand top surface polygons a bit to hide the wall line.
// ORCA: skip if the top surface area is smaller than "min_width_top_surface"
const float top_surface_min_width = std::max<float>(float(ext_perimeter_spacing) / 4.f + scaled<float>(0.00001), float(scale_(config->min_width_top_surface.get_abs_value(unscale_(perimeter_width)))) / 4.f);
// Shrink the polygon to remove the small areas, then expand it back out plus a maragin to hide the wall line a little.
// ORCA: Expand the polygon with half the perimeter width in addition to the contracted amount,
// not the full perimeter width as PS does, to enable thin lettering to print on the top surface without nozzle collisions
// due to thin lines being generated
top = offset2_ex(top, -top_surface_min_width, top_surface_min_width + float(perimeter_width * 0.85));
top_expolygons = offset2_ex(top_expolygons, -top_surface_min_width, top_surface_min_width + float(perimeter_width * 0.85));
// Get final top ExPolygons (bridges were excluded above, so they stay walled).
return intersection_ex(top, contour);
};
top_expolygons = intersection_ex(top_expolygons, infill_contour);
// Walls with the full count, as generated when the single perimeter feature is disabled. Generated on first use.
std::vector<Arachne::VariableWidthLines> full_perimeters;
Polygons full_inner_contour;
bool full_perimeters_generated = false;
auto generate_full_perimeters = [&]() {
if (full_perimeters_generated)
return;
Arachne::WallToolPaths full_tool_paths(last_p, bead_width_0, perimeter_spacing, coord_t(inner_loop_number + 2), wall_0_inset, layer_height, input_params_tmp);
full_perimeters = full_tool_paths.getToolPaths();
full_inner_contour = full_tool_paths.getInnerContour();
full_perimeters_generated = true;
};
// ORCA: the single wall pass allows Arachne 2 beads across a wall, so it fills a wall narrower than 3 outer wall
// widths by widening both, where the full pass adds a middle bead. Over the top surface that is the intent;
// anywhere else it leaves no room for the inner walls. When the single wall pass's outer walls run away from
// the full pass's outside the top surface, take the full pass's outer walls and the area inside them instead.
// Walls closer than outer_wall_tolerance count as the same wall: a widened bead's centerline moves by half
// the width added, and only beads widened by more than twice the tolerance are looked for.
const coord_t outer_wall_tolerance = bead_width_0 / 10;
if (widest_bead(perimeters) > bead_width_0 + 2 * outer_wall_tolerance) {
// The single wall pass's inner contour where it widens no bead: inside nominal width outer walls.
const ExPolygons nominal_infill_contour = offset_ex(last, -float(bead_width_0 + wall_0_inset));
bool nominal_uncovered = false;
// Grown by an outer wall width to take in the outer walls bordering the top surface.
const ExPolygons top_zone = offset_ex(get_top_expolygons(nominal_infill_contour, nominal_uncovered), float(bead_width_0));
if (nominal_uncovered) {
generate_full_perimeters();
if (! full_perimeters.empty() && ! full_perimeters.front().empty() &&
length_off_reference(perimeters, full_perimeters.front(), top_zone, outer_wall_tolerance) > double(perimeter_width)) {
perimeters = { full_perimeters.front() };
infill_contour = diff_ex(nominal_infill_contour, walls_footprint(full_perimeters.front()), ApplySafetyOffset::Yes);
}
}
}
bool uncovered = false;
top_expolygons = get_top_expolygons(infill_contour, uncovered);
if (uncovered) {
// ORCA: onion the real region (inside the outer wall) so the remaining walls follow the actual
// geometry, then cut away the parts over the top surface. Re-onioning the non-top complement
// instead - the fallback when there is no top fill - walls the top/non-top interface and rings
@@ -2711,11 +2612,11 @@ void PerimeterGenerator::process_arachne()
perimeters.insert(perimeters.end(), inner_perimeters.begin(), inner_perimeters.end());
infill_contour = union_ex(top_expolygons, inner_wall_tool_paths.getInnerContour());
} else {
// There is no top surface ExPolygon, so use the walls generated like when the single perimeter
// feature is disabled.
generate_full_perimeters();
perimeters = std::move(full_perimeters);
infill_contour = union_ex(full_inner_contour);
// There is no top surface ExPolygon, so we call Arachne again with parameters
// like when the single perimeter feature is disabled.
Arachne::WallToolPaths no_single_perimeter_tool_paths(last_p, bead_width_0, perimeter_spacing, coord_t(inner_loop_number + 2), wall_0_inset, layer_height, input_params_tmp);
perimeters = no_single_perimeter_tool_paths.getToolPaths();
infill_contour = union_ex(no_single_perimeter_tool_paths.getInnerContour());
}
}
//PS
-2
View File
@@ -58,8 +58,6 @@ public:
static bool parse_color(const std::string& scolor, unsigned char* rgb_out);
static bool parse_color4(const std::string& scolor, unsigned char* rgba_out);
// Rasterizes the SVG into a freshly generated GL texture; the caller owns it and has to delete
// it (ImGuiWrapper::svg_texture() caches the result for the whole session).
static bool load_from_svg_file_change_color(const std::string &filename, unsigned width, unsigned height, ImTextureID &texture_id, const char *hexColor);
+11 -28
View File
@@ -3416,8 +3416,7 @@ void GLCanvas3D::load_sla_preview()
void GLCanvas3D::bind_event_handlers()
{
// Every view switch binds, so binding twice would run each handler twice per event.
if (m_canvas != nullptr && !m_event_handlers_bound) {
if (m_canvas != nullptr) {
m_canvas->Bind(wxEVT_SIZE, &GLCanvas3D::on_size, this);
m_canvas->Bind(wxEVT_IDLE, &GLCanvas3D::on_idle, this);
m_canvas->Bind(wxEVT_CHAR, &GLCanvas3D::on_char, this);
@@ -3427,9 +3426,9 @@ void GLCanvas3D::bind_event_handlers()
m_canvas->Bind(wxEVT_TIMER, &GLCanvas3D::on_timer, this);
m_canvas->Bind(EVT_GLCANVAS_RENDER_TIMER, &GLCanvas3D::on_render_timer, this);
m_toolbar_highlighter.set_timer_owner(m_canvas, 0);
m_canvas->Bind(EVT_GLCANVAS_TOOLBAR_HIGHLIGHTER_TIMER, &GLCanvas3D::on_toolbar_highlighter_timer, this);
m_canvas->Bind(EVT_GLCANVAS_TOOLBAR_HIGHLIGHTER_TIMER, [this](wxTimerEvent&) { m_toolbar_highlighter.blink(); });
m_gizmo_highlighter.set_timer_owner(m_canvas, 0);
m_canvas->Bind(EVT_GLCANVAS_GIZMO_HIGHLIGHTER_TIMER, &GLCanvas3D::on_gizmo_highlighter_timer, this);
m_canvas->Bind(EVT_GLCANVAS_GIZMO_HIGHLIGHTER_TIMER, [this](wxTimerEvent&) { m_gizmo_highlighter.blink(); });
m_canvas->Bind(wxEVT_LEFT_DOWN, &GLCanvas3D::on_mouse, this);
m_canvas->Bind(wxEVT_LEFT_UP, &GLCanvas3D::on_mouse, this);
m_canvas->Bind(wxEVT_MIDDLE_DOWN, &GLCanvas3D::on_mouse, this);
@@ -3444,7 +3443,14 @@ 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, &GLCanvas3D::on_kill_focus, this);
m_canvas->Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent& evt) {
// The key-up that would commit a keyboard edit goes to whatever took the focus.
if (m_selection_edit.kind != SelectionEdit::None)
finish_selection_edit();
ImGui::SetWindowFocus(nullptr);
render();
evt.Skip();
});
m_event_handlers_bound = true;
m_canvas->Bind(wxEVT_GESTURE_PAN, &GLCanvas3D::on_gesture, this);
@@ -3482,9 +3488,6 @@ void GLCanvas3D::unbind_event_handlers()
m_canvas->Unbind(wxEVT_RIGHT_DCLICK, &GLCanvas3D::on_mouse, this);
m_canvas->Unbind(wxEVT_PAINT, &GLCanvas3D::on_paint, this);
m_canvas->Unbind(wxEVT_SET_FOCUS, &GLCanvas3D::on_set_focus, this);
m_canvas->Unbind(wxEVT_KILL_FOCUS, &GLCanvas3D::on_kill_focus, this);
m_canvas->Unbind(EVT_GLCANVAS_TOOLBAR_HIGHLIGHTER_TIMER, &GLCanvas3D::on_toolbar_highlighter_timer, this);
m_canvas->Unbind(EVT_GLCANVAS_GIZMO_HIGHLIGHTER_TIMER, &GLCanvas3D::on_gizmo_highlighter_timer, this);
m_event_handlers_bound = false;
m_canvas->Unbind(wxEVT_GESTURE_PAN, &GLCanvas3D::on_gesture, this);
@@ -5002,26 +5005,6 @@ void GLCanvas3D::on_set_focus(wxFocusEvent& evt)
m_is_touchpad_navigation = wxGetApp().app_config->get_bool("camera_navigation_style");
}
void GLCanvas3D::on_kill_focus(wxFocusEvent& evt)
{
// The key-up that would commit a keyboard edit goes to whatever took the focus.
if (m_selection_edit.kind != SelectionEdit::None)
finish_selection_edit();
ImGui::SetWindowFocus(nullptr);
render();
evt.Skip();
}
void GLCanvas3D::on_toolbar_highlighter_timer(wxTimerEvent& evt)
{
m_toolbar_highlighter.blink();
}
void GLCanvas3D::on_gizmo_highlighter_timer(wxTimerEvent& evt)
{
m_gizmo_highlighter.blink();
}
bool GLCanvas3D::clicked_button_matches_action(const wxMouseEvent& evt, const MouseAction action, const std::map<MouseButton, MouseAction>& mappings) const
{
MouseButton clicked = MouseButton::None;
-3
View File
@@ -1203,9 +1203,6 @@ public:
void on_gesture(wxGestureEvent& evt);
void on_paint(wxPaintEvent& evt);
void on_set_focus(wxFocusEvent& evt);
void on_kill_focus(wxFocusEvent& evt);
void on_toolbar_highlighter_timer(wxTimerEvent& evt);
void on_gizmo_highlighter_timer(wxTimerEvent& evt);
void force_set_focus();
enum class MouseButton { None, Left, Middle, Right };
+6 -13
View File
@@ -25,7 +25,6 @@
#include <string>
#include <map>
#include <vector>
#include <memory>
#include "libslic3r/PrintConfig.hpp"
#include <wx/gdicmn.h>
#include "slic3r/GUI/wxExtensions.hpp"
@@ -2082,15 +2081,9 @@ wxMenu* MenuFactory::instance_menu()
return &m_instance_menu;
}
MenuWithSeparators* MenuFactory::new_transient_menu()
{
m_transient_menu = std::make_unique<MenuWithSeparators>();
return m_transient_menu.get();
}
wxMenu* MenuFactory::layer_menu()
{
MenuWithSeparators* menu = new_transient_menu();
MenuWithSeparators* menu = new MenuWithSeparators();
append_menu_item_settings(menu);
return menu;
@@ -2116,13 +2109,13 @@ wxMenu* MenuFactory::multi_selection_menu()
}
if (all_plates) {
wxMenu* menu = new_transient_menu();
wxMenu* menu = new MenuWithSeparators();
append_menu_item_replace_all_with_stl(menu);
return menu;
}
if (undefined_type)
return nullptr;
wxMenu* menu = new_transient_menu();
wxMenu* menu = new MenuWithSeparators();
if (!multi_volume) {
int index = 0;
if (obj_list()->can_merge_to_multipart_object()) {
@@ -2196,7 +2189,7 @@ wxMenu* MenuFactory::assemble_multi_selection_menu()
// show this menu only for Objects(Instances mixed with Objects)/Volumes selection
return nullptr;
wxMenu* menu = new_transient_menu();
wxMenu* menu = new MenuWithSeparators();
append_menu_item_set_visible(menu);
//append_menu_item_fix_through_cgal(menu);
//append_menu_item_simplify(menu);
@@ -2242,7 +2235,7 @@ wxMenu* MenuFactory::plate_menu()
wxMenu* MenuFactory::assemble_object_menu()
{
wxMenu* menu = new_transient_menu();
wxMenu* menu = new MenuWithSeparators();
// Set Visible
append_menu_item_set_visible(menu);
// Delete
@@ -2262,7 +2255,7 @@ wxMenu* MenuFactory::assemble_object_menu()
wxMenu* MenuFactory::assemble_part_menu()
{
wxMenu* menu = new_transient_menu();
wxMenu* menu = new MenuWithSeparators();
append_menu_item_set_visible(menu);
append_menu_item_delete(menu);
-10
View File
@@ -2,7 +2,6 @@
#define slic3r_GUI_Factories_hpp_
#include <map>
#include <memory>
#include <string>
#include <vector>
#include <array>
@@ -122,12 +121,6 @@ private:
MenuWithSeparators m_assemble_part_menu;
wxMenu m_filament_action_menu;
// The selection dependent menus are rebuilt for every popup, so they cannot be members that
// outlive a build like the ones above; this owns the current one and destroys the previous.
// One slot is enough because PopupMenu() is synchronous: the menu a caller was handed is gone
// from the screen before anything can ask for the next one.
std::unique_ptr<MenuWithSeparators> m_transient_menu;
// Removed/Prepended Items according to the view mode
@@ -135,9 +128,6 @@ private:
std::array<wxMenuItem*, mtCount> items_decrease;
std::array<wxMenuItem*, mtCount> items_set_number_of_copies;
// Replaces m_transient_menu with an empty menu and returns it.
MenuWithSeparators* new_transient_menu();
void create_default_menu();
void create_common_object_menu(wxMenu *menu);
void create_object_menu();
+27 -26
View File
@@ -41,20 +41,19 @@ namespace GUI
ObjectLayers::ObjectLayers(wxWindow* parent) :
OG_Settings(parent, true)
{
m_grid_sizer = new wxFlexGridSizer(5, parent ? parent->FromDIP(2) : 2, wxGetApp().em_unit()); // Title, Min Z, "to", Max Z, buttons sizer
m_grid_sizer = new wxFlexGridSizer(5, 0, wxGetApp().em_unit()); // Title, Min Z, "to", Max Z, unit & buttons sizer
m_grid_sizer->SetFlexibleDirection(wxHORIZONTAL);
m_grid_sizer->AddGrowableCol(1);
m_grid_sizer->AddGrowableCol(3);
m_og->activate();
m_og->sizer->Clear(true);
m_og->sizer->Add(m_grid_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, parent ? parent->FromDIP(2) : 2);
m_og->sizer->Add(m_grid_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, 5);
if (auto stb = dynamic_cast<LabeledStaticBox*>(m_og->stb))
stb->SetCornerRadius(0);
m_bmp_delete = ScalableBitmap(parent, "delete");
m_bmp_add = ScalableBitmap(parent, "add");
m_bmp_layer = ScalableBitmap(parent, "height_range_layer");
}
void ObjectLayers::select_editor(LayerRangeEditor* editor, const bool is_last_edited_range)
@@ -96,14 +95,10 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinus
};
// Add text
auto title_sizer = new wxBoxSizer(wxHORIZONTAL);
auto head_text = new wxStaticText(m_og->ctrl_parent(), wxID_ANY, _L("Range"));
auto head_text = new wxStaticText(m_og->ctrl_parent(), wxID_ANY, _L("Height Range"), wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
head_text->SetBackgroundStyle(wxBG_STYLE_PAINT);
head_text->SetFont(wxGetApp().normal_font());
auto icon = new wxStaticBitmap(m_og->ctrl_parent(), wxID_ANY, m_bmp_layer.bmp());
title_sizer->Add(icon, 0, wxALIGN_CENTER_VERTICAL);
title_sizer->Add(head_text, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, m_og->ctrl_parent()->FromDIP(5));
m_grid_sizer->Add(title_sizer, 0, wxALIGN_CENTER_VERTICAL);
m_grid_sizer->Add(head_text, 0, wxALIGN_CENTER_VERTICAL);
// Add control for the "Min Z"
@@ -131,7 +126,7 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinus
m_grid_sizer->Add(editor, 1, wxEXPAND);
auto middle_text = new wxStaticText(m_og->ctrl_parent(), wxID_ANY, "-");
auto middle_text = new wxStaticText(m_og->ctrl_parent(), wxID_ANY, _L("to"), wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
middle_text->SetBackgroundStyle(wxBG_STYLE_PAINT);
middle_text->SetFont(wxGetApp().normal_font());
m_grid_sizer->Add(middle_text, 0, wxALIGN_CENTER_VERTICAL);
@@ -161,6 +156,11 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinus
m_grid_sizer->Add(editor, 1, wxEXPAND);
auto sizer2 = new wxBoxSizer(wxHORIZONTAL);
auto unit_text = new wxStaticText(m_og->ctrl_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, wxALIGN_CENTER_VERTICAL);
m_grid_sizer->Add(sizer2, 0, wxALIGN_CENTER_VERTICAL);
// BBS
@@ -205,9 +205,8 @@ void ObjectLayers::create_layers_list()
auto sizer = create_layer(range, del_btn, add_btn);
auto b_sizer = new wxBoxSizer(wxHORIZONTAL);
b_sizer->Add(del_btn, 0, wxLEFT, m_og->ctrl_parent()->FromDIP(5));
b_sizer->AddSpacer(m_og->ctrl_parent()->FromDIP(15));
b_sizer->Add(add_btn, 0, wxRIGHT, m_og->ctrl_parent()->FromDIP(5));
b_sizer->Add(del_btn, 0, wxRIGHT | wxLEFT, em_unit(m_parent));
b_sizer->Add(add_btn);
sizer->Add(b_sizer, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP, m_parent->FromDIP(1)); // aligns +/- buttons vertically since we got 1px gap on bottom of icons
del_btn->Bind(wxEVT_BUTTON, [del_btn](wxEvent &) {
@@ -278,7 +277,6 @@ void ObjectLayers::msw_rescale()
{
m_bmp_delete.msw_rescale();
m_bmp_add.msw_rescale();
m_bmp_layer.msw_rescale();
m_grid_sizer->SetHGap(wxGetApp().em_unit());
@@ -369,17 +367,20 @@ LayerRangeEditor::LayerRangeEditor( ObjectLayers* parent,
m_valid_value(value),
m_type(type),
m_set_focus_data(set_focus_data_fn),
TextInput(parent->m_og->ctrl_parent(), value, _L("mm"), "", wxDefaultPosition, wxSize(em_unit(parent->m_parent), wxDefaultCoord), wxTE_PROCESS_ENTER)
wxTextCtrl(parent->m_og->ctrl_parent(), wxID_ANY, value, wxDefaultPosition,
wxSize(em_unit(parent->m_parent), wxDefaultCoord), wxTE_PROCESS_ENTER
#ifdef _WIN32
| wxBORDER_SIMPLE
#endif
)
{
this->SetFont(wxGetApp().normal_font());
wxGetApp().UpdateDarkUI(this);
wxTextCtrl* ctrl = GetTextCtrl();
// Reset m_enter_pressed flag to _false_, when value is editing
ctrl->Bind(wxEVT_TEXT, [this](wxEvent&) { m_enter_pressed = false; }, ctrl->GetId());
this->Bind(wxEVT_TEXT, [this](wxEvent&) { m_enter_pressed = false; }, this->GetId());
ctrl->Bind(wxEVT_TEXT_ENTER, [this, edit_fn](wxCommandEvent& e)
this->Bind(wxEVT_TEXT_ENTER, [this, edit_fn](wxEvent&)
{
m_enter_pressed = true;
// If LayersList wasn't updated/recreated, we can call wxEVT_KILL_FOCUS.Skip()
@@ -394,9 +395,9 @@ LayerRangeEditor::LayerRangeEditor( ObjectLayers* parent,
SetValue(m_valid_value);
m_call_kill_focus = true;
}
}, ctrl->GetId());
}, this->GetId());
ctrl->Bind(wxEVT_KILL_FOCUS, [this, edit_fn](wxFocusEvent& e)
this->Bind(wxEVT_KILL_FOCUS, [this, edit_fn](wxFocusEvent& e)
{
if (!m_enter_pressed) {
#ifndef __WXGTK__
@@ -425,14 +426,14 @@ LayerRangeEditor::LayerRangeEditor( ObjectLayers* parent,
m_call_kill_focus = false;
e.Skip();
}
}, ctrl->GetId());
}, this->GetId());
ctrl->Bind(wxEVT_SET_FOCUS, [this, parent](wxFocusEvent& e)
this->Bind(wxEVT_SET_FOCUS, [this, parent](wxFocusEvent& e)
{
set_focus_data();
parent->update_scene_from_editor_selection();
e.Skip();
}, ctrl->GetId());
}, this->GetId());
#ifdef __WXGTK__ // Workaround! To take information about selectable range
this->Bind(wxEVT_LEFT_DOWN, [this](wxEvent& e)
@@ -446,7 +447,7 @@ LayerRangeEditor::LayerRangeEditor( ObjectLayers* parent,
{
// select all text using Ctrl+A
if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL))
GetTextCtrl()->SetSelection(-1, -1); //select all
this->SetSelection(-1, -1); //select all
event.Skip();
}));
}
@@ -476,7 +477,7 @@ coordf_t LayerRangeEditor::get_value()
void LayerRangeEditor::msw_rescale()
{
Rescale();
SetMinSize(wxSize(wxGetApp().em_unit(), wxDefaultCoord));
}
} //namespace GUI
+1 -6
View File
@@ -9,8 +9,6 @@
#include <wx/sizer.h>
#include <wx/event.h>
#include <slic3r/GUI/Widgets/TextInput.hpp>
#ifdef __WXOSX__
#include "libslic3r/PrintConfig.hpp"
#endif
@@ -36,7 +34,7 @@ enum EditorType
etLayerHeight = 4,
};
class LayerRangeEditor : public TextInput
class LayerRangeEditor : public wxTextCtrl
{
bool m_enter_pressed { false };
bool m_call_kill_focus { false };
@@ -57,8 +55,6 @@ public:
EditorType type() const {return m_type;}
void set_focus_data() const { m_set_focus_data(m_type);}
void SetValue(const wxString& value) {GetTextCtrl()->SetValue(value);}
wxString GetValue() {return GetTextCtrl()->GetValue();}
void msw_rescale();
private:
@@ -69,7 +65,6 @@ class ObjectLayers : public OG_Settings
{
ScalableBitmap m_bmp_delete;
ScalableBitmap m_bmp_add;
ScalableBitmap m_bmp_layer;
ModelObject* m_object {nullptr};
wxFlexGridSizer* m_grid_sizer;
-4
View File
@@ -2317,10 +2317,6 @@ void GLGizmoMeasure::update_measurement_result()
void GLGizmoMeasure::reset_all_pick()
{
std::map<GLVolume*, std::shared_ptr<PickRaycaster>>().swap(m_mesh_raycaster_map);
// register_single_mesh_pick() fills both maps in lockstep, so the measurings have to go with
// the raycasters; otherwise the entries keyed on the GLVolumes of the previous selection stay
// behind for the rest of the session.
std::map<GLVolume*, std::shared_ptr<Measure::Measuring>>().swap(m_mesh_measure_map);
reset_gripper_pick(GripperType::UNDEFINE,true);
}
+3 -4
View File
@@ -37,7 +37,8 @@ enum class PainterGizmoType {
FDM_SUPPORTS,
SEAM,
MM_SEGMENTATION,
FUZZY_SKIN
FUZZY_SKIN,
TEXTURE_DISPLACEMENT
};
class TriangleSelectorGUI : public TriangleSelector {
@@ -111,9 +112,7 @@ class TriangleSelectorPatch : public TriangleSelectorGUI {
public:
explicit TriangleSelectorPatch(const TriangleMesh& mesh, const std::vector<ColorRGBA> ebt_colors, float edge_limit = 0.6f)
: TriangleSelectorGUI(mesh, edge_limit), m_ebt_colors(ebt_colors) {}
// Releases the VAO and the per-patch VBOs built by finalize_triangle_indices(). The base class
// already deletes GL buffers from its GLModel members here, so this needs no context of its own.
virtual ~TriangleSelectorPatch() { release_geometry(); }
virtual ~TriangleSelectorPatch() = default;
// Render current selection. Transformation matrices are supposed
// to be already set.
+4 -44
View File
@@ -385,7 +385,6 @@ ImGuiWrapper::~ImGuiWrapper()
{
//destroy_fonts_texture();
destroy_font();
destroy_svg_textures();
ImGui::DestroyContext();
}
@@ -567,12 +566,6 @@ bool ImGuiWrapper::update_key_data(wxKeyEvent &evt)
return ret;
}
// SVG icons rasterized into GL textures, keyed on file name, size and recolor. Cleared as a whole
// from new_frame() once it grows past MAX_SVG_TEXTURES, which is safe there: the previous frame has
// been rendered and the frame about to be recorded asks for every icon it draws again.
static std::map<std::string, ImTextureID> s_svg_textures;
static const size_t MAX_SVG_TEXTURES = 256;
void ImGuiWrapper::new_frame()
{
if (m_new_frame_open) {
@@ -583,11 +576,6 @@ void ImGuiWrapper::new_frame()
init_font(true);
}
// Recolored icons accumulate one texture per color the session has shown; drop them before
// anything references them again. This frame recreates the handful it actually draws.
if (s_svg_textures.size() > MAX_SVG_TEXTURES)
destroy_svg_textures();
ImGuiIO& io = ImGui::GetIO();
ImGui::NewFrame();
@@ -1840,7 +1828,8 @@ bool menu_item_with_icon(const char *label, const char *shortcut, ImVec2 icon_si
if (icon_color != 0)
ImGui::RenderFrame(icon_pos, icon_pos + icon_size, icon_color);
else {
ImTextureID transparent = ImGuiWrapper::svg_texture(Slic3r::resources_dir() + "/images/transparent.svg", icon_size.x, icon_size.y);
static ImTextureID transparent;
IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/transparent.svg", icon_size.x, icon_size.y, transparent);
window->DrawList->AddImage(transparent, icon_pos, icon_pos + icon_size, { 0,0 }, { 1,1 }, ImGui::GetColorU32(ImVec4(1.f, 1.f, 1.f, 1.f)));
}
}
@@ -3420,36 +3409,6 @@ bool ImGuiWrapper::display_initialized() const
return io.DisplaySize.x >= 0.0f && io.DisplaySize.y >= 0.0f;
}
ImTextureID ImGuiWrapper::svg_texture(const std::string& filename, unsigned width, unsigned height, const char* hex_color)
{
std::string key = filename + "|" + std::to_string(width) + "x" + std::to_string(height);
if (hex_color != nullptr)
key += std::string("|") + hex_color;
const auto it = s_svg_textures.find(key);
if (it != s_svg_textures.end())
return it->second;
ImTextureID texture_id = nullptr;
const bool loaded = (hex_color != nullptr) ?
BitmapCache::load_from_svg_file_change_color(filename, width, height, texture_id, hex_color) :
IMTexture::load_from_svg_file(filename, width, height, texture_id);
if (!loaded)
return nullptr;
s_svg_textures.emplace(std::move(key), texture_id);
return texture_id;
}
void ImGuiWrapper::destroy_svg_textures()
{
for (const auto& texture : s_svg_textures) {
GLuint texture_id = (GLuint)(intptr_t)texture.second;
glsafe(::glDeleteTextures(1, &texture_id));
}
s_svg_textures.clear();
}
void ImGuiWrapper::destroy_font()
{
if (m_font_texture != 0) {
@@ -3529,6 +3488,7 @@ void ImGuiWrapper::filament_group(const std::string& filament_type, const char*
//ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
std::string id = std::to_string(static_cast<unsigned int> (filament_id + 1));
ImDrawList* draw_list = ImGui::GetWindowDrawList();
static ImTextureID transparent;
ImVec2 text_size = ImGui::CalcTextSize(filament_type.c_str());
// BBS image sizing based on text width (DPI scaling)
float img_width = ImGui::CalcTextSize("ABC").x;
@@ -3541,7 +3501,7 @@ void ImGuiWrapper::filament_group(const std::string& filament_type, const char*
if (rgba[3] == 0x00) {
svg_path = "/images/outlined_rect_transparent.svg";
}
ImTextureID transparent = svg_texture(Slic3r::resources_dir() + svg_path, img_size.x, img_size.y, hex_color);
BitmapCache::load_from_svg_file_change_color(Slic3r::resources_dir() + svg_path, img_size.x, img_size.y, transparent, hex_color);
ImGui::BeginGroup();
{
ImVec2 cursor_pos = ImGui::GetCursorScreenPos();
-8
View File
@@ -108,14 +108,6 @@ public:
// Hash of every draw list's vertices, indices and commands.
static ImGuiID draw_data_signature(const ImDrawData* draw_data);
// A GL texture holding an SVG icon rasterized at width x height, optionally recolored.
// Rasterizing an SVG is far too expensive to redo for every frame that draws the icon, and the
// texture the previous frame generated would leak, so the result is kept until the frame that
// finds the cache overgrown drops it (and rebuilds only what it still draws).
static ImTextureID svg_texture(const std::string& filename, unsigned width, unsigned height, const char* hex_color = nullptr);
// Deletes every texture svg_texture() handed out. Requires a current GL context.
static void destroy_svg_textures();
float scaled(float x) const { return x * m_font_size; }
ImVec2 scaled(float x, float y) const { return ImVec2(x * m_font_size, y * m_font_size); }
/// <summary>
+1 -2
View File
@@ -2250,12 +2250,11 @@ void NotificationManager::close_and_delete_self(PopNotification * self)
}
void NotificationManager::remove_notification_of_type(const NotificationType type) {
// Seven notification types may have several instances alive at once, so erase every match:
// stopping at the first one leaves the rest (and the ObjectIDs they hold) behind.
for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end();) {
std::unique_ptr<PopNotification> &notification = *it;
if (notification->get_type() == type) {
it = m_pop_notifications.erase(it);
break;
} else
++it;
}
+1 -4
View File
@@ -1816,10 +1816,7 @@ void InputIpAddressDialog::set_machine_obj(MachineObject* obj)
m_input_printer_name->GetTextCtrl()->SetLabelText(m_obj->get_dev_name());
std::string img_str = DevPrinterConfigUtil::get_printer_connect_help_img(m_obj->printer_type);
if (img_str.empty()) { img_str = "input_access_code_x1"; }
std::string language = wxGetApp().app_config->get("language");
auto diagram_bmp = create_scaled_bitmap(img_str + (language == "zh_CN" ? "_cn" : "_en"), this, 198);
auto diagram_bmp = create_scaled_bitmap(img_str + "_en", this, 198);
m_img_help->SetBitmap(diagram_bmp);
-7
View File
@@ -456,13 +456,6 @@ wxBitmap create_scaled_bitmap( const std::string& bmp_name_in,
const vector<std::string>& array_new_color/* = vector<std::string>*/)//used for semi transparent material)
{
static Slic3r::GUI::BitmapCache cache;
// An empty name means the caller's icon lookup failed
if (bmp_name_in.empty() || bmp_name_in == ".png") {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": empty bitmap name";
return wxNullBitmap;
}
if (bitmap2) {
return create_scaled_bitmap2(bmp_name_in, cache, win, px_cnt, grayscale, resize, array_new_color);
}
-44
View File
@@ -1,20 +1,15 @@
#include <catch2/catch_all.hpp>
#include <catch2/catch_message.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include "libslic3r/libslic3r.h"
#include "libslic3r/Config.hpp"
#include "libslic3r/GCode/GCodeProcessor.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/Utils.hpp"
#include "test_helpers.hpp"
#include "test_utils.hpp"
#include <algorithm>
#include <cstddef>
#include <fstream>
#include "libslic3r/PrintConfig.hpp"
@@ -182,42 +177,3 @@ TEST_CASE("A seam takes the actual speed of the move it follows", "[GCodeProcess
}
REQUIRE(seams > 0);
}
TEST_CASE("Line ends of the exported G-code mark every newline in the file", "[GCodeProcessor]")
{
struct Case
{
const char* name;
bool preheat_backtrace;
bool pre_heating;
};
const auto test_case = GENERATE(values<Case>({
{ "written by size", false, false },
{ "written by time for the preheat backtrace", true, false },
{ "rewritten by the pre-heating pass", false, true },
}));
INFO(test_case.name);
DynamicPrintConfig config = Test::multifilament_config(2, {
{ "single_extruder_multi_material", 0 },
{ "ooze_prevention", test_case.preheat_backtrace },
{ "preheat_time", 30 },
{ "enable_pre_heating", test_case.pre_heating },
});
Print print;
Model model;
const std::vector<std::vector<ConfigBase::SetDeserializeItem>> overrides{ { { "extruder", 1 } }, { { "extruder", 2 } } };
Test::init_print({ Test::cube(20), Test::cube(20) }, print, model, config, &overrides);
GCodeProcessorResult result;
const std::string gcode = Test::gcode(print, &result);
REQUIRE((gcode.find("preheat T") != std::string::npos) == test_case.preheat_backtrace);
REQUIRE((gcode.find(GCodeProcessor::Machine_Start_GCode_End_Tag) != std::string::npos) == test_case.pre_heating);
REQUIRE(gcode.size() > GCodeProcessor::Output_Block_Size);
std::vector<size_t> newline_ends;
for (size_t i = gcode.find('\n'); i != std::string::npos; i = gcode.find('\n', i + 1))
newline_ends.push_back(i + 1);
REQUIRE(result.lines_ends.size() == newline_ends.size());
const auto difference = std::mismatch(result.lines_ends.begin(), result.lines_ends.end(), newline_ends.begin());
INFO("first difference at line " << difference.first - result.lines_ends.begin() + 1);
CHECK(difference.first == result.lines_ends.end());
}
+3 -4
View File
@@ -14,7 +14,6 @@
#include <initializer_list>
#include "libslic3r/Point.hpp"
#include <fstream>
#include <ios>
#include <iterator>
#include <set>
#include <string>
@@ -331,13 +330,13 @@ void init_and_process_print(std::initializer_list<TriangleMesh> meshes, Slic3r::
print.process();
}
std::string gcode(Print & print, GCodeProcessorResult* result)
std::string gcode(Print & print)
{
ScopedTemporaryFile temp(".gcode");
print.set_status_silent();
print.process();
print.export_gcode(temp.string(), result, nullptr);
std::ifstream t(temp.string(), std::ios::binary);
print.export_gcode(temp.string(), nullptr, nullptr);
std::ifstream t(temp.string());
std::string str((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
return str;
}
+3 -5
View File
@@ -17,9 +17,7 @@
#include <unordered_map>
#include <vector>
namespace Slic3r {
struct GCodeProcessorResult;
namespace Test {
namespace Slic3r { namespace Test {
constexpr double MM_PER_MIN = 60.0;
@@ -94,8 +92,8 @@ void init_and_process_print(std::initializer_list<TriangleMesh> meshes, Slic3r::
void init_and_process_print(std::initializer_list<TestMesh> meshes, Slic3r::Print &print, std::initializer_list<Slic3r::ConfigBase::SetDeserializeItem> config_items);
void init_and_process_print(std::initializer_list<TriangleMesh> meshes, Slic3r::Print &print, std::initializer_list<Slic3r::ConfigBase::SetDeserializeItem> config_items);
// Process `print` and return its exported G-code, filling `result` when one is given.
std::string gcode(Print& print, GCodeProcessorResult* result = nullptr);
// Process `print` and return its exported G-code.
std::string gcode(Print& print);
// Build, slice, and return the G-code for `meshes` under the given config.
std::string slice(std::initializer_list<TestMesh> meshes, const DynamicPrintConfig &config);
-120
View File
@@ -19,10 +19,8 @@
#include <cstddef>
#include "libslic3r/libslic3r.h"
#include "libslic3r/BoundingBox.hpp"
#include "libslic3r/ClipperUtils.hpp"
#include "libslic3r/ExPolygon.hpp"
#include "libslic3r/Point.hpp"
#include "libslic3r/Polyline.hpp"
#include <limits>
#include <string>
#include <utility>
@@ -277,124 +275,6 @@ TEST_CASE("Only one wall on the first layer needs a bottom shell", "[Perimeters]
namespace {
// The last layer of the tab, whose top surface shares an island with the tube walls rising past it.
const double tab_top_z = 5.0;
// With the widths below the tube walls are 1.10mm wide once the precise outer wall offset (0.043mm a side) is
// taken off. That is narrower than 3 outer wall spacings (3 x 0.377 = 1.131mm), so an Arachne pass limited to a
// single wall fills it by widening its 2 beads, yet wide enough for the full 2 wall pass to add a middle wall
// (from 1.062mm).
const double narrow_wall = 1.186;
// A 20x30x10 tube with narrow_wall thick walls, and a 20x8x5 tab against its -Y side.
Print &tube_with_tab(Print &print, Model &model, const DynamicPrintConfig &config)
{
ModelObject *object = model.add_object();
object->name = "tube_with_tab.stl";
object->add_volume(make_cube(20., 30., 10.), ModelVolumeType::MODEL_PART, false);
// Overlaps the tube wall by 0.5mm so the two parts slice as one island.
TriangleMesh tab = make_cube(20., 8.5, 5.);
tab.translate(0.f, -8.f, 0.f);
object->add_volume(std::move(tab), ModelVolumeType::MODEL_PART, false);
TriangleMesh bore = make_cube(20. - 2. * narrow_wall, 30. - 2. * narrow_wall, 12.);
bore.translate(float(narrow_wall), float(narrow_wall), -1.f);
object->add_volume(std::move(bore), ModelVolumeType::NEGATIVE_VOLUME, false);
object->add_instance();
object->ensure_on_bed();
print.auto_assign_extruders(object);
print.apply(model, config);
print.validate();
print.set_status_silent();
return print;
}
// Every width the narrow_wall arithmetic depends on, so none of them rests on a default.
DynamicPrintConfig narrow_wall_config(bool only_one_wall_top, double top_surface_expansion)
{
DynamicPrintConfig config = base_config("arachne");
config.set_deserialize_strict({
{ "wall_loops", 2 },
{ "nozzle_diameter", "0.4" },
{ "line_width", 0.42 },
{ "outer_wall_line_width", 0.42 },
{ "inner_wall_line_width", 0.45 },
{ "min_bead_width", "85%" },
{ "precise_outer_wall", true },
{ "wall_sequence", "inner wall/outer wall" },
{ "only_one_wall_top", only_one_wall_top },
{ "top_surface_expansion", top_surface_expansion },
});
return config;
}
// Inner wall length the layer at print_z extrudes within 3mm of its +Y edge: the tube wall facing away from the tab.
double far_wall_inner_wall_length(const Print &print, double print_z)
{
for (const Layer *layer : print.objects().front()->layers()) {
if (std::abs(layer->print_z - print_z) > EPSILON)
continue;
BoundingBox band = get_extents(layer->lslices);
band.min.y() = band.max.y() - scaled<coord_t>(3.);
Polylines inner_walls;
auto collect = [&inner_walls](const ExtrusionPaths &paths) {
for (const ExtrusionPath &path : paths)
if (path.role() == erPerimeter)
inner_walls.emplace_back(path.as_polyline());
};
for (const LayerRegion *region : layer->regions()) {
const ExtrusionEntityCollection walls = region->perimeters.flatten();
for (const ExtrusionEntity *entity : walls.entities) {
if (const auto *loop = dynamic_cast<const ExtrusionLoop*>(entity))
collect(loop->paths);
else if (const auto *multi_path = dynamic_cast<const ExtrusionMultiPath*>(entity))
collect(multi_path->paths);
else if (const auto *path = dynamic_cast<const ExtrusionPath*>(entity))
collect({ *path });
}
}
return unscaled<double>(total_length(intersection_pl(inner_walls, band.polygon())));
}
return 0.;
}
} // namespace
// only_one_wall_top first lays out an island with a single Arachne wall and generates the inner walls inside it.
// On a wall narrower than 3 outer wall spacings that single wall pass widens its 2 beads to fill the wall and leaves
// no room for the middle wall, which is only intended over the top surface. The tube walls away from the tab are not
// under the tab's top surface, so on the tab's last layer they keep the inner wall they get with the option off.
TEST_CASE("Only one wall on top surfaces keeps the inner walls of narrow walls away from the top surface", "[Perimeters]")
{
// 0 re-onions the region beside the top surface, 2 clips the inner walls over it.
const double top_surface_expansion = GENERATE(0.0, 2.0);
CAPTURE(top_surface_expansion);
struct TabTopLayer {
double perimeters;
double far_wall_inner_walls;
};
auto tab_top_layer_for = [top_surface_expansion](bool only_one_wall_top) {
Print print;
Model model;
tube_with_tab(print, model, narrow_wall_config(only_one_wall_top, top_surface_expansion));
print.process();
REQUIRE_FALSE(print.objects().empty());
return TabTopLayer{ perimeter_length_at(print, tab_top_z), far_wall_inner_wall_length(print, tab_top_z) };
};
const TabTopLayer plain = tab_top_layer_for(false);
const TabTopLayer one_wall = tab_top_layer_for(true);
// The option acts on this layer: the inner walls under the tab's top surface are gone.
REQUIRE(plain.far_wall_inner_walls > 10.);
CHECK(one_wall.perimeters < plain.perimeters);
CHECK_THAT(one_wall.far_wall_inner_walls, Catch::Matchers::WithinAbs(plain.far_wall_inner_walls, 1.0));
}
namespace {
// The layer that closes the cavity of box_over_cavity(), the first one printed over air.
const double cavity_ceiling_z = 6.2;