diff --git a/resources/info/legacy_compatible_printers.json b/resources/info/legacy_compatible_printers.json new file mode 100644 index 0000000000..2f3e249d0a --- /dev/null +++ b/resources/info/legacy_compatible_printers.json @@ -0,0 +1,795 @@ +{ + "presets": { + "Bambu ABS @BBL P1P": [ + "Bambu Lab P1P 0.6 nozzle" + ], + "Bambu ABS @BBL X1C": [ + "Bambu Lab P1S 0.4 nozzle", + "Bambu Lab P1S 0.6 nozzle", + "Bambu Lab X1 Carbon 0.6 nozzle" + ], + "Bambu ASA @BBL X2D": [ + "Bambu Lab X2D 0.8 nozzle" + ], + "Bambu PA6-CF @BBL H2D": [ + "Bambu Lab H2D 0.6 nozzle", + "Bambu Lab H2D 0.8 nozzle" + ], + "Bambu PA6-CF @BBL H2DP": [ + "Bambu Lab H2D Pro 0.6 nozzle", + "Bambu Lab H2D Pro 0.8 nozzle" + ], + "Bambu PA6-CF @BBL H2S": [ + "Bambu Lab H2S 0.6 nozzle", + "Bambu Lab H2S 0.8 nozzle" + ], + "Bambu PA6-CF @BBL P2S": [ + "Bambu Lab P2S 0.6 nozzle", + "Bambu Lab P2S 0.8 nozzle" + ], + "Bambu PA6-CF @BBL X1C": [ + "Bambu Lab P1P 0.6 nozzle", + "Bambu Lab P1P 0.8 nozzle", + "Bambu Lab P1S 0.6 nozzle", + "Bambu Lab P1S 0.8 nozzle", + "Bambu Lab X1 Carbon 0.6 nozzle", + "Bambu Lab X1 Carbon 0.8 nozzle" + ], + "Bambu PA6-CF @BBL X2D": [ + "Bambu Lab X2D 0.8 nozzle" + ], + "Bambu PAHT-CF @BBL H2D": [ + "Bambu Lab H2D 0.6 nozzle", + "Bambu Lab H2D 0.8 nozzle" + ], + "Bambu PAHT-CF @BBL H2DP": [ + "Bambu Lab H2D Pro 0.6 nozzle", + "Bambu Lab H2D Pro 0.8 nozzle" + ], + "Bambu PAHT-CF @BBL H2S": [ + "Bambu Lab H2S 0.6 nozzle", + "Bambu Lab H2S 0.8 nozzle" + ], + "Bambu PAHT-CF @BBL P1P": [ + "Bambu Lab P1P 0.6 nozzle", + "Bambu Lab P1P 0.8 nozzle" + ], + "Bambu PAHT-CF @BBL P2S": [ + "Bambu Lab P2S 0.6 nozzle", + "Bambu Lab P2S 0.8 nozzle" + ], + "Bambu PAHT-CF @BBL X1C": [ + "Bambu Lab P1S 0.6 nozzle", + "Bambu Lab P1S 0.8 nozzle", + "Bambu Lab X1 Carbon 0.6 nozzle", + "Bambu Lab X1 Carbon 0.8 nozzle" + ], + "Bambu PAHT-CF @BBL X2D": [ + "Bambu Lab X2D 0.8 nozzle" + ], + "Bambu PC @BBL P1P": [ + "Bambu Lab P1P 0.6 nozzle" + ], + "Bambu PETG @0.2 nozzle": [ + "Qidi Q1 Pro 0.2 nozzle" + ], + "Bambu PETG Basic @BBL H2D 0.6 nozzle": [ + "Bambu Lab H2D 0.8 nozzle" + ], + "Bambu PETG Basic @BBL H2DP 0.6 nozzle": [ + "Bambu Lab H2D Pro 0.8 nozzle" + ], + "Bambu PETG Basic @BBL H2S": [ + "Bambu Lab H2S 0.6 nozzle", + "Bambu Lab H2S 0.8 nozzle" + ], + "Bambu PETG Basic @BBL P2S 0.6 nozzle": [ + "Bambu Lab P2S 0.8 nozzle" + ], + "Bambu PETG Basic @BBL X2D": [ + "Bambu Lab X2D 0.8 nozzle" + ], + "Bambu PETG HF @BBL P2S 0.6 nozzle": [ + "Bambu Lab P2S 0.8 nozzle" + ], + "Bambu PETG HF @BBL X1C": [ + "Bambu Lab P1P 0.4 nozzle", + "Bambu Lab P1S 0.4 nozzle" + ], + "Bambu PETG HF @BBL X1C 0.8 nozzle": [ + "Bambu Lab X1 Carbon 0.6 nozzle" + ], + "Bambu PETG HF @BBL X2D": [ + "Bambu Lab X2D 0.8 nozzle" + ], + "Bambu PETG Translucent @BBL H2D 0.6 nozzle": [ + "Bambu Lab H2D 0.8 nozzle" + ], + "Bambu PETG Translucent @BBL H2DP 0.6 nozzle": [ + "Bambu Lab H2D Pro 0.8 nozzle" + ], + "Bambu PETG Translucent @BBL H2S": [ + "Bambu Lab H2S 0.6 nozzle", + "Bambu Lab H2S 0.8 nozzle" + ], + "Bambu PETG Translucent @BBL P2S 0.6 nozzle": [ + "Bambu Lab P2S 0.8 nozzle" + ], + "Bambu PETG Translucent @BBL X1C 0.8 nozzle": [ + "Bambu Lab P1P 0.6 nozzle", + "Bambu Lab P1S 0.6 nozzle", + "Bambu Lab X1 Carbon 0.6 nozzle" + ], + "Bambu PETG-CF @BBL H2D 0.6 nozzle": [ + "Bambu Lab H2D 0.8 nozzle" + ], + "Bambu PETG-CF @BBL H2DP 0.6 nozzle": [ + "Bambu Lab H2D Pro 0.8 nozzle" + ], + "Bambu PETG-CF @BBL H2S": [ + "Bambu Lab H2S 0.6 nozzle", + "Bambu Lab H2S 0.8 nozzle" + ], + "Bambu PETG-CF @BBL P2S 0.6 nozzle": [ + "Bambu Lab P2S 0.8 nozzle" + ], + "Bambu PLA Basic @BBL P1P": [ + "Bambu Lab P1P 0.6 nozzle" + ], + "Bambu PLA Basic @BBL X1C": [ + "Bambu Lab P1S 0.4 nozzle", + "Bambu Lab P1S 0.6 nozzle", + "Bambu Lab X1 Carbon 0.6 nozzle" + ], + "Bambu PLA Basic @BBL X2D 0.6 nozzle": [ + "Bambu Lab X2D 0.8 nozzle" + ], + "Bambu PLA Dynamic @BBL P1P": [ + "Bambu Lab P1P 0.6 nozzle" + ], + "Bambu PLA Dynamic @BBL X1C": [ + "Bambu Lab P1S 0.6 nozzle", + "Bambu Lab X1 Carbon 0.6 nozzle" + ], + "Bambu PLA Galaxy @BBL P1P": [ + "Bambu Lab P1P 0.6 nozzle" + ], + "Bambu PLA Galaxy @BBL X1C": [ + "Bambu Lab P1S 0.6 nozzle", + "Bambu Lab X1 Carbon 0.6 nozzle" + ], + "Bambu PLA Glow @BBL P1P": [ + "Bambu Lab P1P 0.6 nozzle" + ], + "Bambu PLA Glow @BBL X1C": [ + "Bambu Lab P1S 0.6 nozzle", + "Bambu Lab X1 Carbon 0.6 nozzle" + ], + "Bambu PLA Lite @BBL H2D": [ + "Bambu Lab H2D 0.6 nozzle" + ], + "Bambu PLA Lite @BBL H2S": [ + "Bambu Lab H2S 0.6 nozzle", + "Bambu Lab H2S 0.8 nozzle" + ], + "Bambu PLA Lite @BBL P1P": [ + "Bambu Lab P1P 0.6 nozzle" + ], + "Bambu PLA Lite @BBL X1C": [ + "Bambu Lab P1S 0.6 nozzle", + "Bambu Lab X1 Carbon 0.6 nozzle" + ], + "Bambu PLA Lite @BBL X2D": [ + "Bambu Lab X2D 0.8 nozzle" + ], + "Bambu PLA Marble @BBL P1P": [ + "Bambu Lab P1P 0.6 nozzle", + "Bambu Lab P1P 0.8 nozzle" + ], + "Bambu PLA Marble @BBL X1C": [ + "Bambu Lab P1S 0.6 nozzle", + "Bambu Lab X1 Carbon 0.6 nozzle" + ], + "Bambu PLA Matte @BBL P1P": [ + "Bambu Lab P1P 0.6 nozzle" + ], + "Bambu PLA Matte @BBL X1C": [ + "Bambu Lab P1S 0.4 nozzle", + "Bambu Lab P1S 0.6 nozzle", + "Bambu Lab X1 Carbon 0.6 nozzle" + ], + "Bambu PLA Matte @BBL X2D": [ + "Bambu Lab X2D 0.8 nozzle" + ], + "Bambu PLA Metal @BBL P1P": [ + "Bambu Lab P1P 0.6 nozzle" + ], + "Bambu PLA Metal @BBL X1C": [ + "Bambu Lab P1S 0.6 nozzle", + "Bambu Lab X1 Carbon 0.6 nozzle" + ], + "Bambu PLA Silk+ @BBL H2D": [ + "Bambu Lab H2D 0.6 nozzle", + "Bambu Lab H2D 0.8 nozzle" + ], + "Bambu PLA Silk+ @BBL H2DP": [ + "Bambu Lab H2D Pro 0.6 nozzle", + "Bambu Lab H2D Pro 0.8 nozzle" + ], + "Bambu PLA Silk+ @BBL H2S": [ + "Bambu Lab H2S 0.6 nozzle", + "Bambu Lab H2S 0.8 nozzle" + ], + "Bambu PLA Silk+ @BBL P1P": [ + "Bambu Lab P1P 0.6 nozzle" + ], + "Bambu PLA Silk+ @BBL X1C": [ + "Bambu Lab P1S 0.4 nozzle", + "Bambu Lab P1S 0.6 nozzle", + "Bambu Lab X1 Carbon 0.6 nozzle" + ], + "Bambu PLA Silk+ @BBL X2D": [ + "Bambu Lab X2D 0.8 nozzle" + ], + "Bambu PLA Sparkle @BBL P1P": [ + "Bambu Lab P1P 0.6 nozzle" + ], + "Bambu PLA Sparkle @BBL X1C": [ + "Bambu Lab P1S 0.6 nozzle", + "Bambu Lab X1 Carbon 0.6 nozzle" + ], + "Bambu PLA Tough+ @BBL P1P": [ + "Bambu Lab P1P 0.6 nozzle" + ], + "Bambu PLA Tough+ @BBL X1C": [ + "Bambu Lab X1 Carbon 0.6 nozzle" + ], + "Bambu PLA Tough+ @BBL X2D": [ + "Bambu Lab X2D 0.8 nozzle" + ], + "Bambu PLA Translucent @BBL H2D 0.8 nozzle": [ + "Bambu Lab H2D 0.6 nozzle" + ], + "Bambu PLA Translucent @BBL H2DP 0.8 nozzle": [ + "Bambu Lab H2D Pro 0.6 nozzle" + ], + "Bambu PLA Translucent @BBL H2S": [ + "Bambu Lab H2S 0.6 nozzle", + "Bambu Lab H2S 0.8 nozzle" + ], + "Bambu PLA Wood @BBL P1P": [ + "Bambu Lab P1P 0.6 nozzle" + ], + "Bambu PLA Wood @BBL X1C": [ + "Bambu Lab P1S 0.6 nozzle", + "Bambu Lab X1 Carbon 0.6 nozzle" + ], + "Bambu PLA-CF @BBL H2D 0.6 nozzle": [ + "Bambu Lab H2D 0.8 nozzle" + ], + "Bambu PLA-CF @BBL H2DP 0.6 nozzle": [ + "Bambu Lab H2D Pro 0.8 nozzle" + ], + "Bambu PLA-CF @BBL H2S": [ + "Bambu Lab H2S 0.6 nozzle", + "Bambu Lab H2S 0.8 nozzle" + ], + "Bambu PLA-CF @BBL P2S 0.6 nozzle": [ + "Bambu Lab P2S 0.8 nozzle" + ], + "Bambu PLA-CF @BBL X2D": [ + "Bambu Lab X2D 0.8 nozzle" + ], + "Bambu PPA-CF @BBL X1C": [ + "Bambu Lab P1P 0.6 nozzle", + "Bambu Lab P1P 0.8 nozzle", + "Bambu Lab P1S 0.6 nozzle", + "Bambu Lab P1S 0.8 nozzle", + "Bambu Lab X1 Carbon 0.6 nozzle", + "Bambu Lab X1 Carbon 0.8 nozzle" + ], + "Bambu PVA @BBL H2D": [ + "Bambu Lab H2D 0.6 nozzle", + "Bambu Lab H2D 0.8 nozzle" + ], + "Bambu PVA @BBL H2DP": [ + "Bambu Lab H2D Pro 0.6 nozzle", + "Bambu Lab H2D Pro 0.8 nozzle" + ], + "Bambu PVA @BBL H2S": [ + "Bambu Lab H2S 0.6 nozzle", + "Bambu Lab H2S 0.8 nozzle" + ], + "Bambu PVA @BBL P2S": [ + "Bambu Lab P2S 0.6 nozzle", + "Bambu Lab P2S 0.8 nozzle" + ], + "Bambu PVA @BBL X2D": [ + "Bambu Lab X2D 0.8 nozzle" + ], + "Bambu Support For PLA @BBL H2D": [ + "Bambu Lab H2D 0.6 nozzle", + "Bambu Lab H2D 0.8 nozzle" + ], + "Bambu Support For PLA @BBL H2DP": [ + "Bambu Lab H2D Pro 0.6 nozzle", + "Bambu Lab H2D Pro 0.8 nozzle" + ], + "Bambu Support For PLA @BBL H2S": [ + "Bambu Lab H2S 0.6 nozzle", + "Bambu Lab H2S 0.8 nozzle" + ], + "Bambu Support For PLA @BBL P2S": [ + "Bambu Lab P2S 0.6 nozzle", + "Bambu Lab P2S 0.8 nozzle" + ], + "Bambu Support For PLA @BBL X2D": [ + "Bambu Lab X2D 0.8 nozzle" + ], + "Bambu Support For PLA/PETG @BBL H2D": [ + "Bambu Lab H2D 0.6 nozzle", + "Bambu Lab H2D 0.8 nozzle" + ], + "Bambu Support For PLA/PETG @BBL H2DP": [ + "Bambu Lab H2D Pro 0.6 nozzle", + "Bambu Lab H2D Pro 0.8 nozzle" + ], + "Bambu Support For PLA/PETG @BBL H2S": [ + "Bambu Lab H2S 0.6 nozzle", + "Bambu Lab H2S 0.8 nozzle" + ], + "Bambu Support For PLA/PETG @BBL P2S": [ + "Bambu Lab P2S 0.6 nozzle", + "Bambu Lab P2S 0.8 nozzle" + ], + "Bambu Support For PLA/PETG @BBL X2D": [ + "Bambu Lab X2D 0.8 nozzle" + ], + "Bambu Support for ABS @BBL H2D": [ + "Bambu Lab H2D 0.6 nozzle", + "Bambu Lab H2D 0.8 nozzle" + ], + "Bambu Support for ABS @BBL H2DP": [ + "Bambu Lab H2D Pro 0.6 nozzle", + "Bambu Lab H2D Pro 0.8 nozzle" + ], + "Bambu Support for ABS @BBL H2S": [ + "Bambu Lab H2S 0.6 nozzle", + "Bambu Lab H2S 0.8 nozzle" + ], + "Bambu Support for ABS @BBL P2S": [ + "Bambu Lab P2S 0.6 nozzle", + "Bambu Lab P2S 0.8 nozzle" + ], + "Bambu Support for ABS @BBL X2D": [ + "Bambu Lab X2D 0.8 nozzle" + ], + "Bambu TPU 90A @BBL H2D": [ + "Bambu Lab H2D 0.6 nozzle", + "Bambu Lab H2D 0.8 nozzle" + ], + "Bambu TPU 90A @BBL H2DP": [ + "Bambu Lab H2D Pro 0.6 nozzle", + "Bambu Lab H2D Pro 0.8 nozzle" + ], + "Bambu TPU 95A HF @BBL H2D": [ + "Bambu Lab H2D 0.4 nozzle", + "Bambu Lab H2D 0.8 nozzle" + ], + "Bambu TPU 95A HF @BBL H2DP": [ + "Bambu Lab H2D Pro 0.4 nozzle", + "Bambu Lab H2D Pro 0.8 nozzle" + ], + "Bambu TPU 95A HF @BBL H2S": [ + "Bambu Lab H2S 0.6 nozzle", + "Bambu Lab H2S 0.8 nozzle" + ], + "Bambu TPU 95A HF @BBL P2S": [ + "Bambu Lab P2S 0.6 nozzle", + "Bambu Lab P2S 0.8 nozzle" + ], + "Bambu TPU 95A HF @BBL X2D": [ + "Bambu Lab X2D 0.8 nozzle" + ], + "Bambu TPU for AMS @BBL H2D": [ + "Bambu Lab H2D 0.6 nozzle", + "Bambu Lab H2D 0.8 nozzle" + ], + "Bambu TPU for AMS @BBL H2DP": [ + "Bambu Lab H2D Pro 0.6 nozzle", + "Bambu Lab H2D Pro 0.8 nozzle" + ], + "Bambu TPU for AMS @BBL H2S": [ + "Bambu Lab H2S 0.6 nozzle", + "Bambu Lab H2S 0.8 nozzle" + ], + "Bambu TPU for AMS @BBL P1P": [ + "Bambu Lab P1P 0.6 nozzle" + ], + "Bambu TPU for AMS @BBL P2S": [ + "Bambu Lab P2S 0.6 nozzle", + "Bambu Lab P2S 0.8 nozzle" + ], + "Bambu TPU for AMS @BBL X1C": [ + "Bambu Lab P1S 0.6 nozzle", + "Bambu Lab X1 Carbon 0.6 nozzle" + ], + "Bambu TPU for AMS @BBL X2D": [ + "Bambu Lab X2D 0.8 nozzle" + ], + "Blocks ABS @Blocks_RD50_V2": [ + "BLOCKS RF50 0.4 nozzle", + "BLOCKS RF50 0.6 nozzle", + "BLOCKS RF50 0.8 nozzle" + ], + "Blocks ASA @Blocks_RD50_V2": [ + "BLOCKS RF50 0.4 nozzle", + "BLOCKS RF50 0.6 nozzle", + "BLOCKS RF50 0.8 nozzle" + ], + "Blocks ASA-CF @Blocks_RD50_V2": [ + "BLOCKS RF50 0.4 nozzle", + "BLOCKS RF50 0.6 nozzle", + "BLOCKS RF50 0.8 nozzle" + ], + "Blocks PA @Blocks_RD50_V2": [ + "BLOCKS RF50 0.4 nozzle", + "BLOCKS RF50 0.6 nozzle", + "BLOCKS RF50 0.8 nozzle" + ], + "Blocks PA-CF @Blocks_RD50_V2": [ + "BLOCKS RF50 0.4 nozzle", + "BLOCKS RF50 0.6 nozzle", + "BLOCKS RF50 0.8 nozzle" + ], + "Blocks PC @Blocks_RD50_V2": [ + "BLOCKS RF50 0.4 nozzle", + "BLOCKS RF50 0.6 nozzle", + "BLOCKS RF50 0.8 nozzle" + ], + "Blocks PETG @Blocks_Pro_S100": [ + "BLOCKS RD50 V2 0.4 nozzle", + "BLOCKS RD50 V2 0.6 nozzle", + "BLOCKS RD50 V2 0.8 nozzle", + "BLOCKS RF50 0.4 nozzle", + "BLOCKS RF50 0.6 nozzle", + "BLOCKS RF50 0.8 nozzle" + ], + "Blocks PLA @Blocks_Pro_S100": [ + "BLOCKS RD50 V2 0.4 nozzle", + "BLOCKS RD50 V2 0.6 nozzle", + "BLOCKS RD50 V2 0.8 nozzle", + "BLOCKS RF50 0.4 nozzle", + "BLOCKS RF50 0.6 nozzle", + "BLOCKS RF50 0.8 nozzle" + ], + "Blocks PLA-CF @Blocks_RD50_V2": [ + "BLOCKS RF50 0.4 nozzle", + "BLOCKS RF50 0.6 nozzle", + "BLOCKS RF50 0.8 nozzle" + ], + "Blocks PVA @Blocks_RD50_V2": [ + "BLOCKS RF50 0.4 nozzle", + "BLOCKS RF50 0.6 nozzle", + "BLOCKS RF50 0.8 nozzle" + ], + "Blocks TPU @Blocks_Pro_S100": [ + "BLOCKS RD50 V2 0.4 nozzle", + "BLOCKS RD50 V2 0.6 nozzle", + "BLOCKS RD50 V2 0.8 nozzle", + "BLOCKS RF50 0.4 nozzle", + "BLOCKS RF50 0.6 nozzle", + "BLOCKS RF50 0.8 nozzle" + ], + "Cubicon ABS @Cubicon xCeler-I 0.4 nozzle": [ + "Cubicon xCeler-Plus 0.4 nozzle" + ], + "Cubicon ABS-A100 @Cubicon xCeler-I 0.4 nozzle": [ + "Cubicon xCeler-Plus 0.4 nozzle" + ], + "Cubicon ABSk @Cubicon xCeler-I 0.4 nozzle": [ + "Cubicon xCeler-Plus 0.4 nozzle" + ], + "Cubicon PA-CF @Cubicon xCeler-I 0.4 nozzle": [ + "Cubicon xCeler-Plus 0.4 nozzle" + ], + "Cubicon PC @Cubicon xCeler-I 0.4 nozzle": [ + "Cubicon xCeler-Plus 0.4 nozzle" + ], + "Cubicon PETG @Cubicon xCeler-I 0.4 nozzle": [ + "Cubicon xCeler-Plus 0.4 nozzle" + ], + "Cubicon PLA @Cubicon xCeler-I 0.4 nozzle": [ + "Cubicon xCeler-Plus 0.4 nozzle" + ], + "Cubicon PLA+ @Cubicon xCeler-I 0.4 nozzle": [ + "Cubicon xCeler-Plus 0.4 nozzle" + ], + "Cubicon PLAi21 @Cubicon xCeler-I 0.4 nozzle": [ + "Cubicon xCeler-Plus 0.4 nozzle" + ], + "Flashforge HS PLA": [ + "Flashforge AD5X 0.4 nozzle", + "Flashforge AD5X 0.6 nozzle", + "Flashforge AD5X 0.8 nozzle" + ], + "Flashforge PETG-CF": [ + "Flashforge AD5X 0.4 nozzle", + "Flashforge AD5X 0.6 nozzle", + "Flashforge AD5X 0.8 nozzle" + ], + "Flashforge PLA-CF": [ + "Flashforge AD5X 0.4 nozzle", + "Flashforge AD5X 0.6 nozzle", + "Flashforge AD5X 0.8 nozzle" + ], + "Flashforge TPU 95A": [ + "Flashforge AD5X 0.4 nozzle", + "Flashforge AD5X 0.6 nozzle", + "Flashforge AD5X 0.8 nozzle" + ], + "FusRock PAHT-CF": [ + "Flashforge Guider 3 Ultra 0.6 Nozzle" + ], + "FusRock PET-CF": [ + "Flashforge Guider 3 Ultra 0.6 Nozzle" + ], + "Generic ABS @Anycubic": [ + "Anycubic Kobra 3 0.4 nozzle" + ], + "Generic ABS @Creality": [ + "Creality CR-10 SE 0.2 nozzle", + "Creality CR-10 SE 0.4 nozzle", + "Creality CR-10 SE 0.6 nozzle", + "Creality CR-10 SE 0.8 nozzle", + "Creality CR-6 SE 0.2 nozzle", + "Creality CR-6 SE 0.4 nozzle", + "Creality CR-6 SE 0.6 nozzle", + "Creality CR-6 SE 0.8 nozzle", + "Creality CR-M4 0.4 nozzle", + "Creality Ender-3 V2 0.4 nozzle" + ], + "Generic ABS @Creality Ender-3V3-all": [ + "Creality Ender-3 V3 Plus 0.4 nozzle", + "Creality Ender-3 V3 Plus 0.6 nozzle" + ], + "Generic ABS @Creality Hi-all": [ + "Creality Hi 0.4 nozzle" + ], + "Generic ABS @Creality K1-all": [ + "Creality K1 SE 0.4 nozzle", + "Creality K1C 0.4 nozzle" + ], + "Generic ABS @Creality K2-all": [ + "Creality K2 0.4 nozzle", + "Creality K2 Plus 0.4 nozzle", + "Creality K2 Plus 0.6 nozzle", + "Creality K2 Plus 0.8 nozzle", + "Creality K2 Pro 0.4 nozzle" + ], + "Generic ABS @Flashforge G3U": [ + "Flashforge Guider 3 Ultra 0.6 Nozzle" + ], + "Generic ASA @Creality Ender-3V3-all": [ + "Creality Ender-3 V3 Plus 0.4 nozzle", + "Creality Ender-3 V3 Plus 0.6 nozzle" + ], + "Generic ASA @Creality K1-all": [ + "Creality K1 SE 0.4 nozzle", + "Creality K1C 0.4 nozzle" + ], + "Generic ASA @Creality K2-all": [ + "Creality K2 0.4 nozzle", + "Creality K2 Plus 0.4 nozzle", + "Creality K2 Pro 0.4 nozzle" + ], + "Generic ASA @Flashforge G3U": [ + "Flashforge Guider 3 Ultra 0.6 Nozzle" + ], + "Generic HIPS @Flashforge": [ + "Flashforge Guider 3 Ultra 0.6 Nozzle" + ], + "Generic PA @Prusa": [ + "Prusa MINIIS 0.25 nozzle", + "Prusa MINIIS 0.4 nozzle", + "Prusa MINIIS 0.6 nozzle", + "Prusa MINIIS 0.8 nozzle" + ], + "Generic PA-CF @Creality Ender-3V3-all": [ + "Creality Ender-3 V3 Plus 0.4 nozzle", + "Creality Ender-3 V3 Plus 0.6 nozzle" + ], + "Generic PA-CF @Creality K1-all": [ + "Creality K1C 0.4 nozzle" + ], + "Generic PA-CF @Creality K2-all": [ + "Creality K2 Plus 0.4 nozzle", + "Creality K2 Plus 0.6 nozzle", + "Creality K2 Plus 0.8 nozzle" + ], + "Generic PA-CF @Prusa": [ + "Prusa MINIIS 0.25 nozzle", + "Prusa MINIIS 0.4 nozzle", + "Prusa MINIIS 0.6 nozzle", + "Prusa MINIIS 0.8 nozzle" + ], + "Generic PC @Creality K1-all": [ + "Creality K1C 0.4 nozzle" + ], + "Generic PETG @Creality": [ + "Creality CR-6 SE 0.2 nozzle", + "Creality CR-6 SE 0.4 nozzle", + "Creality CR-6 SE 0.6 nozzle", + "Creality CR-6 SE 0.8 nozzle", + "Creality CR-M4 0.4 nozzle" + ], + "Generic PETG @Creality Ender-3V3-all": [ + "Creality Ender-3 V3 0.4 nozzle", + "Creality Ender-3 V3 0.6 nozzle", + "Creality Ender-3 V3 Plus 0.4 nozzle", + "Creality Ender-3 V3 Plus 0.6 nozzle", + "Creality Ender-3 V3 SE 0.2 nozzle", + "Creality Ender-3 V3 SE 0.4 nozzle", + "Creality Ender-3 V3 SE 0.6 nozzle", + "Creality Ender-3 V3 SE 0.8 nozzle" + ], + "Generic PETG @Creality Hi-all": [ + "Creality Hi 0.4 nozzle" + ], + "Generic PETG @Creality K1-all": [ + "Creality K1 SE 0.4 nozzle", + "Creality K1C 0.4 nozzle" + ], + "Generic PETG @Creality K2-all": [ + "Creality K2 0.4 nozzle", + "Creality K2 Plus 0.4 nozzle", + "Creality K2 Plus 0.6 nozzle", + "Creality K2 Plus 0.8 nozzle", + "Creality K2 Pro 0.4 nozzle" + ], + "Generic PETG @Flashforge G3U": [ + "Flashforge Guider 3 Ultra 0.6 Nozzle", + "Flashforge Guider 3 Ultra 0.8 Nozzle" + ], + "Generic PETG @Sovol Zero Hardened Steel nozzle": [ + "Sovol Zero 0.4 nozzle" + ], + "Generic PETG HF @BBL X1C": [ + "Bambu Lab P1S 0.4 nozzle", + "Bambu Lab P1S 0.6 nozzle", + "Bambu Lab X1 0.4 nozzle", + "Bambu Lab X1 0.6 nozzle", + "Bambu Lab X1 0.8 nozzle" + ], + "Generic PETG-CF @Flashforge G3U": [ + "Flashforge Guider 3 Ultra 0.6 Nozzle", + "Flashforge Guider 3 Ultra 0.8 Nozzle" + ], + "Generic PLA @Creality": [ + "Creality CR-10 SE 0.2 nozzle", + "Creality CR-10 SE 0.4 nozzle", + "Creality CR-10 SE 0.6 nozzle", + "Creality CR-10 SE 0.8 nozzle", + "Creality CR-6 SE 0.2 nozzle", + "Creality CR-6 SE 0.4 nozzle", + "Creality CR-6 SE 0.6 nozzle", + "Creality CR-6 SE 0.8 nozzle", + "Creality CR-M4 0.4 nozzle" + ], + "Generic PLA @Creality Ender-3V3-all": [ + "Creality Ender-3 V3 0.4 nozzle", + "Creality Ender-3 V3 0.6 nozzle", + "Creality Ender-3 V3 Plus 0.4 nozzle", + "Creality Ender-3 V3 Plus 0.6 nozzle" + ], + "Generic PLA @Creality K1-all": [ + "Creality K1 SE 0.4 nozzle", + "Creality K1C 0.4 nozzle" + ], + "Generic PLA @Creality K2-all": [ + "Creality K2 0.4 nozzle", + "Creality K2 Plus 0.4 nozzle", + "Creality K2 Plus 0.6 nozzle", + "Creality K2 Plus 0.8 nozzle", + "Creality K2 Pro 0.4 nozzle" + ], + "Generic PLA @Flashforge G3U": [ + "Flashforge Guider 3 Ultra 0.6 Nozzle", + "Flashforge Guider 3 Ultra 0.8 Nozzle" + ], + "Generic PLA @Hi-all": [ + "Creality Hi 0.4 nozzle", + "Creality Hi 0.6 nozzle" + ], + "Generic PLA @Sovol Zero Hardened Steel nozzle": [ + "Sovol Zero 0.4 nozzle" + ], + "Generic PLA Silk @Sovol Zero Hardened Steel nozzle": [ + "Sovol Zero 0.4 nozzle" + ], + "Generic PLA-CF @Creality K1-all": [ + "Creality K1 SE 0.4 nozzle", + "Creality K1C 0.4 nozzle" + ], + "Generic PLA-CF @Creality K2-all": [ + "Creality K2 0.4 nozzle", + "Creality K2 Plus 0.4 nozzle", + "Creality K2 Pro 0.4 nozzle" + ], + "Generic PLA-CF @Flashforge G3U": [ + "Flashforge Guider 3 Ultra 0.6 Nozzle", + "Flashforge Guider 3 Ultra 0.8 Nozzle" + ], + "Generic PLA-CF @Prusa": [ + "Prusa MINIIS 0.25 nozzle", + "Prusa MINIIS 0.4 nozzle", + "Prusa MINIIS 0.6 nozzle", + "Prusa MINIIS 0.8 nozzle" + ], + "Generic TPU @Anycubic": [ + "Anycubic Kobra 3 0.4 nozzle" + ], + "Generic TPU @Creality": [ + "Creality CR-10 SE 0.2 nozzle", + "Creality CR-10 SE 0.4 nozzle", + "Creality CR-10 SE 0.6 nozzle", + "Creality CR-10 SE 0.8 nozzle" + ], + "Generic TPU @Creality Ender-3V3-all": [ + "Creality Ender-3 V3 0.4 nozzle", + "Creality Ender-3 V3 0.6 nozzle", + "Creality Ender-3 V3 Plus 0.4 nozzle", + "Creality Ender-3 V3 Plus 0.6 nozzle", + "Creality Ender-3 V3 SE 0.2 nozzle", + "Creality Ender-3 V3 SE 0.4 nozzle", + "Creality Ender-3 V3 SE 0.6 nozzle", + "Creality Ender-3 V3 SE 0.8 nozzle" + ], + "Generic TPU @Creality K1-all": [ + "Creality K1 SE 0.4 nozzle", + "Creality K1C 0.4 nozzle" + ], + "Generic TPU @Creality K2-all": [ + "Creality K2 0.4 nozzle", + "Creality K2 Plus 0.4 nozzle", + "Creality K2 Pro 0.4 nozzle" + ], + "HATCHBOX PETG @0.2 nozzle": [ + "Qidi Q1 Pro 0.2 nozzle" + ], + "QIDI PETG Basic @0.2 nozzle": [ + "Qidi Q1 Pro 0.2 nozzle" + ], + "QIDI PETG Rapido @0.2 nozzle": [ + "Qidi Q1 Pro 0.2 nozzle" + ], + "QIDI PETG Translucent @0.2 nozzle": [ + "Qidi Q1 Pro 0.2 nozzle" + ], + "re3D PC @0.4 nozzle": [ + "re3D Gigabot 4 0.8 nozzle", + "re3D Gigabot 4 XLT 0.8 nozzle", + "re3D Terabot 4 0.8 nozzle" + ], + "re3D PETG @0.4 nozzle": [ + "re3D Gigabot 4 0.8 nozzle", + "re3D Gigabot 4 XLT 0.8 nozzle", + "re3D Terabot 4 0.8 nozzle" + ], + "re3D PLA @0.4 nozzle": [ + "re3D Gigabot 4 0.8 nozzle", + "re3D Gigabot 4 XLT 0.8 nozzle", + "re3D Terabot 4 0.8 nozzle" + ], + "re3D rPETG @0.8 nozzle": [ + "re3D GigabotX 2 1.75 nozzle", + "re3D GigabotX 2 XLT 1.75 nozzle", + "re3D TerabotX 2 1.75 nozzle" + ], + "re3D rPP @0.8 nozzle": [ + "re3D GigabotX 2 1.75 nozzle", + "re3D GigabotX 2 XLT 1.75 nozzle", + "re3D TerabotX 2 1.75 nozzle" + ] + } +} diff --git a/scripts/orca_profile_tool.py b/scripts/orca_profile_tool.py index ec94eb7653..405a594906 100755 --- a/scripts/orca_profile_tool.py +++ b/scripts/orca_profile_tool.py @@ -11,6 +11,7 @@ commands: normalize rewrite profile files into their canonical shape trim delete profile files no .json list references update-index regenerate the *_list sections of .json + legacy-compat write the printers user presets keep from an older release options shared by several commands: --vendor VENDOR act on one vendor bundle only; repeatable, empty means all @@ -24,6 +25,9 @@ After adding, renaming or deleting profile files, run: normalize -> update-index -> generate-id -> check normalize supplies missing types; update-index registers presets before id generation. +After renaming filament presets or narrowing their compatible_printers, also +rerun legacy-compat --base <2.4.2 profiles> so user presets made against 2.4.2 +keep their printers. Use trim only for deliberate cleanup, previewed with --dry-run: it judges against the current index and can delete newly added, unindexed presets. @@ -3072,6 +3076,131 @@ def fix_variant_arrays(profiles_dir=PROFILES_DIR, vendors=None, dry_run=False, s return len(changed), errors +# --------------------------------------------------------------------------- +# legacy-compat +# --------------------------------------------------------------------------- + +LEGACY_COMPAT_PATH = os.path.normpath( + os.path.join(SCRIPTS_DIR, "..", "resources", "info", "legacy_compatible_printers.json")) + + +def _renamed_from(data): + """A preset's renamed_from names. Profiles write one ';'-separated string, as the + loader's unescape_strings_cstyle reads it; none of them quote or escape a name.""" + value = data.get("renamed_from") or [] + return [n for v in _as_list(value) for n in v.split(";") if n] + + +def _flatten_presets(profiles_dir): + """Every bundle's composed filament and printer presets, for legacy-compat. + + Returns (filaments, printers): filaments maps (vendor, name) to a dict with the + resolved compatible_printers, filament_id, extruder variants, renamed_from and + whether it is instantiated; printers maps each instantiated printer name to a + dict with its renamed_from and extruder variants. instantiation is read from the + preset's own file, as the loader requires every file to state it. + """ + def instantiated(data): + return str(data.get("instantiation", "")).lower() == "true" + + ofl = load_vendor_configs(profiles_dir, OFL)["filament"] + filaments, printers = {}, {} + for vendor in list_vendor_names(profiles_dir): + configs = load_vendor_configs(profiles_dir, vendor) + cache = {} + for name, (_rel, data) in configs["filament"].items(): + config = compose_config(name, configs["filament"], ofl, cache, vendor == OFL) + filaments[(vendor, name)] = { + "compatible_printers": set(_as_list(config.get("compatible_printers", ([],))[0])), + "filament_id": config.get("filament_id", (None,))[0], + "variants": set(_as_list(config.get("filament_extruder_variant", ([],))[0])), + "instantiation": instantiated(data), + "renamed_from": _renamed_from(data), + } + cache = {} + for name, (_rel, data) in configs["machine"].items(): + if instantiated(data): + config = compose_config(name, configs["machine"], {}, cache) + printers[name] = { + "renamed_from": _renamed_from(data), + "variants": set(_as_list(config.get("printer_extruder_variant", ([],))[0])), + } + return filaments, printers + + +def legacy_compatible_printers(base_dir, profiles_dir=PROFILES_DIR): + """Printers each current system filament preset covered in the base release + but no longer lists, kept only where a current preset with the same + filament_id still covers that printer. + + User presets store only their diff, so they inherit their parent's + compatible_printers; when a profile change narrows that list, a user preset + made against the base release disappears from printers it was used on. The + loader adds these printers back to such user presets. Requiring a same-id + successor leaves out printers a filament was deliberately taken off, and + requiring a shared extruder variant keeps a printer from picking up another + variant's values. + + Keyed by the current preset name: the loader resolves a renamed parent + before the map is consulted. Printer names are translated through the + printers' renamed_from. Returns {preset name: sorted printer names}. + """ + old_filaments, _ = _flatten_presets(base_dir) + new_filaments, new_printers = _flatten_presets(profiles_dir) + renamed_printer = {old: new for new, rec in new_printers.items() for old in rec["renamed_from"]} + + old_by_name = {} + for (vendor, name), rec in old_filaments.items(): + old_by_name.setdefault(name, []).append((vendor, rec)) + + covering = {} # filament_id -> printers some current instantiated preset covers + for rec in new_filaments.values(): + if rec["instantiation"] and rec["filament_id"]: + covering.setdefault(rec["filament_id"], set()).update(rec["compatible_printers"]) + + def shares_variant(filament, printer): + variants = new_printers[printer]["variants"] + return not filament["variants"] or not variants or bool(filament["variants"] & variants) + + result = {} + for (vendor, name), rec in sorted(new_filaments.items()): + # An empty list means every printer, so nothing can have been lost. + if not rec["instantiation"] or not rec["compatible_printers"]: + continue + old_printers = set() + for old_name in [name] + rec["renamed_from"]: + candidates = old_by_name.get(old_name, []) + same_vendor = [r for v, r in candidates if v == vendor] + for old in same_vendor or [r for _, r in candidates]: + old_printers |= old["compatible_printers"] + old_printers = {renamed_printer.get(p, p) for p in old_printers} + lost = old_printers - rec["compatible_printers"] + lost &= covering.get(rec["filament_id"], set()) & set(new_printers) + lost = {p for p in lost if shares_variant(rec, p)} + if lost: + result[name] = sorted(lost) + return result + + +def run_legacy_compat(base_dir, profiles_dir, output, check): + presets = legacy_compatible_printers(base_dir, profiles_dir) + if check: + try: + with open(output, encoding="utf-8") as f: + current = json.load(f).get("presets") + except (OSError, ValueError, AttributeError): + current = None + if current != presets: + print_error(f"{output} is out of date; rerun legacy-compat --base {base_dir}") + return 1 + print_success(f"{output} is up to date") + return 0 + with open(output, "w", encoding="utf-8", newline="\n") as f: + f.write(json.dumps({"presets": presets}, indent=4, sort_keys=True, ensure_ascii=False) + "\n") + print_info(f"wrote {len(presets)} presets / {sum(map(len, presets.values()))} printers to {output}") + return 0 + + # --------------------------------------------------------------------------- # CLI # --------------------------------------------------------------------------- @@ -3093,11 +3222,17 @@ examples: preview resizing the variant arrays check rejects in that bundle orca_profile_tool.py check --strict --vendor BBL also hold every preset's inherited variant arrays to its own width + orca_profile_tool.py legacy-compat --base /tmp/v2.4.2/resources/profiles + rewrite resources/info/legacy_compatible_printers.json against 2.4.2's + profiles (git archive v2.4.2 resources/profiles | tar -x -C /tmp/v2.4.2) after adding, renaming or deleting profile files, run in this order: normalize -> update-index -> generate-id -> check normalize supplies missing types; update-index registers presets before id generation. +After renaming filament presets or narrowing their compatible_printers, also +rerun legacy-compat --base <2.4.2 profiles> so user presets made against 2.4.2 +keep their printers. Use trim only for deliberate cleanup, previewed with --dry-run: it judges against the current index and can delete newly added, unindexed presets. """ @@ -3227,6 +3362,22 @@ def build_parser(): "Use trim only for deliberate unindexed-file cleanup, previewed with\n" "--dry-run; it can also delete newly authored presets.") + legacy_cmd = add( + "legacy-compat", [profiles_opt], + "write the printers user presets keep from an older release", + "Compare the tree with an older release's profiles and write, per current\n" + "filament preset, the printers it covered then and no longer lists, where a\n" + "preset with the same filament_id still covers them. The loader adds these\n" + "to user presets that inherit the preset, so a narrowed compatible_printers\n" + "does not hide them on printers they were made for.") + legacy_cmd.add_argument("--base", required=True, metavar="DIR", + help="the older release's resources/profiles directory") + legacy_cmd.add_argument("--output", default=LEGACY_COMPAT_PATH, metavar="FILE", + help="map to write (default: resources/info/" + "legacy_compatible_printers.json)") + legacy_cmd.add_argument("--check", action="store_true", + help="fail if the map differs from what --base implies; write nothing") + return parser @@ -3260,6 +3411,9 @@ def main(argv=None): return run_generate_id(profiles_dir, vendors, args.filament_id, args.setting_id, args.dry_run) + if args.command == "legacy-compat": + return run_legacy_compat(args.base, profiles_dir, args.output, args.check) + if args.command == "fix-variant": _changed, errors = fix_variant_arrays(profiles_dir, vendors, dry_run=args.dry_run, strict=args.strict) diff --git a/scripts/tests/test_profile_tool.py b/scripts/tests/test_profile_tool.py index d32dbd144e..c2c37c4612 100644 --- a/scripts/tests/test_profile_tool.py +++ b/scripts/tests/test_profile_tool.py @@ -1258,6 +1258,97 @@ class TestVariantNames(TreeCase): self.assertIn('holds the legacy variant "Direct Drive Normal"', out) +# --------------------------------------------------------------------------- +# legacy-compat +# --------------------------------------------------------------------------- + +class TestLegacyCompat(unittest.TestCase): + """A base release and the current tree, each with one vendor.""" + + def setUp(self): + self.base, self.cur = Tree(), Tree() + self.addCleanup(self.base.cleanup) + self.addCleanup(self.cur.cleanup) + + @staticmethod + def printer(tree, name, **extra): + tree.write("V", f"machine/{name}.json", + {"type": "machine", "name": name, "instantiation": "true", **extra}) + tree.index("V", "machine", name, f"machine/{name}.json") + + @staticmethod + def filament(tree, name, printers, filament_id="OFpetg01", **extra): + tree.write("V", f"filament/{name}.json", + {"type": "filament", "name": name, "instantiation": "true", + "filament_id": filament_id, "compatible_printers": printers, **extra}) + tree.index("V", "filament", name, f"filament/{name}.json") + + def generate(self): + return apt.legacy_compatible_printers(self.base.profiles, self.cur.profiles) + + def test_renamed_and_narrowed_preset_keeps_printers_a_same_id_sibling_covers(self): + for tree in (self.base, self.cur): + for p in ("A 0.4", "A 0.6", "B 0.4"): + self.printer(tree, p) + self.filament(self.base, "PETG @A-all", ["A 0.4", "A 0.6", "B 0.4"]) + self.filament(self.cur, "PETG @V A-all", ["A 0.6"], renamed_from="PETG @A-all;PETG A-all") + self.filament(self.cur, "PETG @A 0.4", ["A 0.4"]) + self.assertEqual(self.generate(), {"PETG @V A-all": ["A 0.4"]}) + + def test_printer_without_a_successor_is_left_out(self): + for tree in (self.base, self.cur): + for p in ("A 0.4", "A 0.6"): + self.printer(tree, p) + self.filament(self.base, "PETG @A", ["A 0.4", "A 0.6"]) + self.filament(self.cur, "PETG @A", ["A 0.6"]) + self.filament(self.cur, "Other @A", ["A 0.4"], filament_id="OFother1") + self.assertEqual(self.generate(), {}) + + def test_printer_names_follow_printer_renames(self): + self.printer(self.base, "Old 0.4") + self.printer(self.base, "A 0.6") + self.printer(self.cur, "New 0.4", renamed_from="Old 0.4") + self.printer(self.cur, "A 0.6") + self.filament(self.base, "PETG @A", ["Old 0.4", "A 0.6"]) + self.filament(self.cur, "PETG @A", ["A 0.6"]) + self.filament(self.cur, "PETG @New", ["New 0.4"]) + self.assertEqual(self.generate(), {"PETG @A": ["New 0.4"]}) + + def test_printer_without_a_shared_extruder_variant_is_left_out(self): + for tree in (self.base, self.cur): + self.printer(tree, "Direct 0.4", printer_extruder_variant=["Direct Drive Standard"]) + self.printer(tree, "Bowden 0.4", printer_extruder_variant=["Bowden Standard"]) + self.filament(self.base, "PETG @all", ["Direct 0.4", "Bowden 0.4"]) + self.filament(self.cur, "PETG @all", ["Direct 0.4"], + filament_extruder_variant=["Direct Drive Standard"]) + self.filament(self.cur, "PETG @Bowden", ["Bowden 0.4"]) + self.assertEqual(self.generate(), {}) + + def test_preset_now_listing_no_printer_is_skipped(self): + for tree in (self.base, self.cur): + self.printer(tree, "A 0.4") + self.filament(self.base, "PETG @A", ["A 0.4"]) + self.filament(self.cur, "PETG @A", []) + self.assertEqual(self.generate(), {}) + + def test_check_passes_on_a_current_map_and_fails_on_a_stale_one(self): + for tree in (self.base, self.cur): + for p in ("A 0.4", "A 0.6"): + self.printer(tree, p) + self.filament(self.base, "PETG @A", ["A 0.4", "A 0.6"]) + self.filament(self.cur, "PETG @A", ["A 0.6"]) + self.filament(self.cur, "PETG @A 0.4", ["A 0.4"]) + output = os.path.join(self.cur.dir, "map.json") + argv = ["legacy-compat", "--base", self.base.profiles, "--profiles", self.cur.profiles, + "--output", output] + with contextlib.redirect_stdout(io.StringIO()): + self.assertEqual(apt.main(argv), 0) + self.assertEqual(apt.main(argv + ["--check"]), 0) + with open(output, "w", encoding="utf-8") as f: + json.dump({"presets": {}}, f) + self.assertEqual(apt.main(argv + ["--check"]), 1) + + class TestDispatch(TreeCase): def test_each_command_reaches_its_own_writer(self): self.t.write("V", "filament/A.json", {"type": "filament", "name": "A"}) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 78fa0785ef..ef34da08f9 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -932,10 +932,14 @@ bool is_compatible_with_printer(const PresetWithVendorProfile &preset, const Pre return true; } } + const std::vector &legacy = preset.preset.legacy_compatible_printers; + auto is_legacy_printer = [&legacy](const std::string &name) { return std::find(legacy.begin(), legacy.end(), name) != legacy.end(); }; return preset.preset.is_default || active_printer.preset.name.empty() || !has_compatible_printers || std::find(compatible_printers->values.begin(), compatible_printers->values.end(), active_printer.preset.name) != compatible_printers->values.end() || - (!active_printer.preset.is_system && is_compatible_with_parent_printer(preset, active_printer)); + (!active_printer.preset.is_system && is_compatible_with_parent_printer(preset, active_printer)) || + is_legacy_printer(active_printer.preset.name) || + (!active_printer.preset.is_system && is_legacy_printer(active_printer.preset.inherits())); } bool is_compatible_with_printer(const PresetWithVendorProfile &preset, const PresetWithVendorProfile &active_printer) @@ -2728,6 +2732,42 @@ void PresetCollection::update_after_user_presets_loaded() return; } +const Preset *PresetCollection::system_parent_with_inherited_printers(const Preset &preset) const +{ + if (!preset.is_user() || preset.inherits().empty()) + return nullptr; + const Preset *parent = get_preset_parent(preset); + if (parent == nullptr || !parent->is_system) + return nullptr; + // A user preset that sets its own compatible_printers keeps exactly those. + const auto *own = preset.config.option("compatible_printers"); + const auto *inherited = parent->config.option("compatible_printers"); + return own != nullptr && inherited != nullptr && own->values == inherited->values ? parent : nullptr; +} + +void PresetCollection::set_legacy_compatible_printers(const std::map> &printers_by_parent) +{ + lock(); + for (Preset &preset : m_presets) { + preset.legacy_compatible_printers.clear(); + const Preset *parent = system_parent_with_inherited_printers(preset); + if (parent == nullptr) + continue; + // The map names the parent as it was when the map was written; a later rename keeps that name in renamed_from. + auto it = printers_by_parent.find(parent->name); + for (auto old_name = parent->renamed_from.begin(); it == printers_by_parent.end() && old_name != parent->renamed_from.end(); ++old_name) + it = printers_by_parent.find(*old_name); + if (it == printers_by_parent.end()) + continue; + preset.legacy_compatible_printers = it->second; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": user preset " << preset.name << " keeps " << it->second.size() + << " printer(s) its parent " << parent->name << " listed in an older release"; + } + if (m_idx_selected < m_presets.size() && m_edited_preset.name == m_presets[m_idx_selected].name) + m_edited_preset.legacy_compatible_printers = m_presets[m_idx_selected].legacy_compatible_printers; + unlock(); +} + //BBS: validate_preset bool PresetCollection::validate_preset(const std::string &preset_name, std::string &inherit_name) { @@ -3081,6 +3121,7 @@ bool PresetCollection::clone_presets(std::vector const &presets, preset.vendor = nullptr; preset.renamed_from.clear(); preset.m_excluded_from.clear(); + preset.legacy_compatible_printers.clear(); preset.setting_id.clear(); preset.inherits().clear(); preset.is_default = false; @@ -3218,6 +3259,9 @@ void PresetCollection::save_current_preset(const std::string &new_name, bool det preset.m_excluded_from.clear(); BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": save preset %1% , with detach")%new_name; } + // Legacy printers belong only to a preset that still inherits its system parent's printer list. + if (system_parent_with_inherited_printers(preset) == nullptr) + preset.legacy_compatible_printers.clear(); //BBS: add lock logic for sync preset in background if (m_type == Preset::TYPE_PRINT) @@ -3270,6 +3314,8 @@ void PresetCollection::save_current_preset(const std::string &new_name, bool det } else preset.is_project_embedded = false; + if (system_parent_with_inherited_printers(preset) == nullptr) + preset.legacy_compatible_printers.clear(); if (m_type == Preset::TYPE_PRINT) preset.config.option("print_settings_id", true)->value = new_name; else if (m_type == Preset::TYPE_FILAMENT) diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index f06c8f4224..58d0a4514a 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -311,6 +311,10 @@ public: // certain printer models defined in the vendor profile as well, in this case we want to hide this generic preset for these printer models. std::set m_excluded_from; + // Printers a user filament preset is also compatible with because its system parent listed them in + // an older release; see PresetCollection::set_legacy_compatible_printers(). + std::vector legacy_compatible_printers; + // Orca: flag to indicate if this preset is from Orca Filament Library bool m_from_orca_filament_lib = false; @@ -601,6 +605,11 @@ public: void save_user_presets(const std::string& dir_path, const std::string& type, std::map& need_to_delete_list); bool load_user_preset(std::string name, std::map preset_values, PresetsConfigSubstitutions& substitutions, ForwardCompatibilitySubstitutionRule rule, const PresetOrigin &load_origin = PresetOrigin(PresetOrigin::Kind::User)); void update_after_user_presets_loaded(); + // User presets store only their diff, so a user preset inherits its system parent's compatible_printers + // and disappears from a printer once a profile update narrows that list. Give each user preset that + // inherits the list the printers its parent covered in an older release (printers_by_parent, keyed by the + // parent's current name), so it keeps showing on the printers it was made for. + void set_legacy_compatible_printers(const std::map> &printers_by_parent); //BBS: get user presets int get_user_presets(PresetBundle *preset_bundle, std::vector &result_presets); void set_sync_info_and_save(std::string name, std::string setting_id, std::string syncinfo, long long update_time, const std::string& user_id); @@ -893,6 +902,10 @@ public: size_t num_default_presets() { return m_num_default_presets; } protected: + // The system parent of a user preset whose compatible_printers is the parent's, or nullptr: the presets + // set_legacy_compatible_printers() gives legacy printers to. + const Preset* system_parent_with_inherited_printers(const Preset &preset) const; + PresetCollection() = default; // Deleted by the std::recursive_mutex member. PresetBundle copies by assignment. PresetCollection(const PresetCollection &other) = delete; diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 8215cb3aef..e183134954 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -96,6 +97,34 @@ using json = nlohmann::json; namespace Slic3r { +// Per current system filament preset, the printers it listed in an older release and no longer does +// (resources/info/legacy_compatible_printers.json, written by orca_profile_tool.py legacy-compat). +// The file ships with the build, so it is read once it opens; until then (e.g. presets loaded before +// resources_dir() is set) every call tries again and there are no legacy printers. +static std::map> legacy_compatible_printers() +{ + static std::mutex mutex; + static std::map> printers_by_parent; + static bool read = false; + + std::lock_guard guard(mutex); + if (!read) { + const fs::path path = fs::path(resources_dir()) / "info" / "legacy_compatible_printers.json"; + boost::nowide::ifstream in(path.string()); + if (!in) { + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": " << path.string() << " not found"; + return {}; + } + read = true; + try { + printers_by_parent = json::parse(in).at("presets").get>>(); + } catch (const std::exception &err) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": can not read " << path.string() << ": " << err.what(); + } + } + return printers_by_parent; +} + // Project-level options imported from a loaded 3MF into project_config. s_project_options_published // below is the reduced subset that crosses over in "published" 3MF mode; keep both in sync. // s_project_options_published additionally carries wipe_tower_rotation_angle, which normal @@ -889,6 +918,7 @@ PresetsConfigSubstitutions PresetBundle::load_presets(AppConfig &config, Forward // raw vendor-JSON references instead of the silently-repaired ones. if (!validation_mode) this->normalize_compatible_presets(); + filaments.set_legacy_compatible_printers(legacy_compatible_printers()); this->update_multi_material_filament_presets(); this->update_compatible(PresetSelectCompatibleType::Never); @@ -1517,6 +1547,7 @@ PresetsConfigSubstitutions PresetBundle::load_user_presets(AppConfig & // Rewrite renamed compatible references in synced user presets before compatibility is evaluated. this->normalize_compatible_presets(); + filaments.set_legacy_compatible_printers(legacy_compatible_printers()); this->update_multi_material_filament_presets(); this->update_compatible(PresetSelectCompatibleType::Never); @@ -2523,6 +2554,7 @@ PresetsConfigSubstitutions PresetBundle::update_subscribed_presets( // Rewrite renamed compatible references in synced user presets before compatibility is evaluated. this->normalize_compatible_presets(); + filaments.set_legacy_compatible_printers(legacy_compatible_printers()); this->update_multi_material_filament_presets(); this->update_compatible(PresetSelectCompatibleType::Never); diff --git a/tests/libslic3r/test_preset_bundle_loading.cpp b/tests/libslic3r/test_preset_bundle_loading.cpp index 02d607650f..55be8f7be0 100644 --- a/tests/libslic3r/test_preset_bundle_loading.cpp +++ b/tests/libslic3r/test_preset_bundle_loading.cpp @@ -7161,3 +7161,89 @@ TEST_CASE("A user filament whose derived compatible printer cannot be written ba CHECK(read_file(file) == before); } #endif + +TEST_CASE("A user filament keeps the printers its narrowed system parent listed in an older release", "[Preset][Bundle]") +{ + PresetBundle bundle; + PresetCollection &filaments = bundle.filaments; + const std::string parent = "Generic PETG @Creality K1-all"; + + // load_preset inserts in sorted order, so presets are looked up again rather than held by reference. + auto set_printers = [&filaments](const std::string &name, std::vector printers) { + filaments.find_preset(name)->config.option("compatible_printers", true)->values = std::move(printers); + }; + add_inmemory_preset(filaments, parent).is_system = true; + add_inmemory_preset(filaments, "My PETG", parent); + add_inmemory_preset(filaments, "My PETG own list", parent); + set_printers(parent, { "Creality K1C 0.6 nozzle" }); + set_printers("My PETG", { "Creality K1C 0.6 nozzle" }); + set_printers("My PETG own list", { "Creality K1C 0.6 nozzle", "Creality K1C 0.8 nozzle" }); + + filaments.set_legacy_compatible_printers({ { parent, { "Creality K1C 0.4 nozzle" } } }); + + auto compatible = [&filaments](const std::string &filament, const Preset &printer) { + return is_compatible_with_printer(PresetWithVendorProfile(*filaments.find_preset(filament), nullptr), + PresetWithVendorProfile(printer, nullptr)); + }; + Preset k1c_04(Preset::TYPE_PRINTER, "Creality K1C 0.4 nozzle"); + k1c_04.is_system = true; + Preset k1_max(Preset::TYPE_PRINTER, "Creality K1 Max 0.4 nozzle"); + k1_max.is_system = true; + Preset my_k1c(Preset::TYPE_PRINTER, "My K1C"); + my_k1c.config.option(BBL_JSON_KEY_INHERITS, true)->value = "Creality K1C 0.4 nozzle"; + + CHECK(compatible("My PETG", k1c_04)); + CHECK(compatible("My PETG", my_k1c)); + CHECK_FALSE(compatible("My PETG", k1_max)); + // The system preset itself and a user preset with its own list keep exactly their lists. + CHECK_FALSE(compatible(parent, k1c_04)); + CHECK_FALSE(compatible("My PETG own list", k1c_04)); + + // A parent renamed after the map was written is still found under its old name. + add_inmemory_preset(filaments, "Generic PETG @Creality K1 renamed").is_system = true; + add_inmemory_preset(filaments, "My renamed PETG", "Generic PETG @Creality K1 renamed"); + set_renamed_from(filaments, "Generic PETG @Creality K1 renamed", { parent }); + set_printers("Generic PETG @Creality K1 renamed", { "Creality K1C 0.6 nozzle" }); + set_printers("My renamed PETG", { "Creality K1C 0.6 nozzle" }); + filaments.set_legacy_compatible_printers({ { parent, { "Creality K1C 0.4 nozzle" } } }); + CHECK(compatible("My renamed PETG", k1c_04)); +} + +TEST_CASE("A saved user filament keeps its legacy printers only while it inherits its parent's printer list", "[Preset][Bundle]") +{ + ScopedTemporaryDir temp_dir; + PresetBundle bundle; + PresetCollection &filaments = bundle.filaments; + const std::string parent = "Generic PETG @Creality K1-all"; + add_inmemory_preset(filaments, parent).is_system = true; + filaments.find_preset(parent)->config.option("compatible_printers", true)->values = { "Creality K1C 0.6 nozzle" }; + // Like a real user preset, the file states only its diff, so the printer list comes from the parent. + fs::create_directories(temp_dir.path() / PRESET_FILAMENT_NAME); + std::ofstream((temp_dir.path() / PRESET_FILAMENT_NAME / "My PETG.json").string()) + << R"({"type":"filament","name":"My PETG","from":"User","version":"1.0.0","inherits":")" << parent << R"("})"; + PresetsConfigSubstitutions substitutions; + filaments.load_presets(temp_dir.path().string(), PRESET_FILAMENT_NAME, substitutions, + ForwardCompatibilitySubstitutionRule::EnableSilent); + filaments.set_legacy_compatible_printers({ { parent, { "Creality K1C 0.4 nozzle" } } }); + const std::vector legacy { "Creality K1C 0.4 nozzle" }; + REQUIRE(filaments.find_preset("My PETG")->legacy_compatible_printers == legacy); + + auto save_from_my_petg = [&filaments](const std::string &name, bool detach) { + filaments.select_preset_by_name("My PETG", true); + filaments.save_current_preset(name, detach); + return filaments.find_preset(name)->legacy_compatible_printers; + }; + CHECK(save_from_my_petg("My PETG copy", false) == legacy); + CHECK(save_from_my_petg("My PETG detached", true).empty()); + + // A clone is detached from the parent, as "Create filament" makes for another printer. + std::vector failures; + REQUIRE(filaments.clone_presets({ filaments.find_preset("My PETG") }, failures, + [](Preset &preset, Preset::Type &) { preset.name = "My PETG clone"; })); + CHECK(filaments.find_preset("My PETG clone")->legacy_compatible_printers.empty()); + + filaments.select_preset_by_name("My PETG", true); + filaments.get_edited_preset().config.option("compatible_printers")->values = { "Creality K1C 0.8 nozzle" }; + filaments.save_current_preset("My PETG", false); + CHECK(filaments.find_preset("My PETG")->legacy_compatible_printers.empty()); +}