diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index 406fd93c80..9add73b4dc 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -82,6 +82,29 @@ variants = default technology = SLA family = SL1 +[default_filaments] +Generic PLA = 1 +Generic PLA MMU2 = 1 +Prusa PLA = 1 +Prusa PLA MMU2 = 1 +Prusament PLA = 1 +Prusament PLA MMU2 = 1 + +[default_sla_materials] +Prusa Azure Blue Tough 0.05 = 1 +Prusa Black Tough 0.05 = 1 +Prusa Green Casting 0.05 = 1 +Prusa Grey Tough 0.05 = 1 +Prusa Maroon Tough 0.05 = 1 +Prusa Orange Tough 0.025 = 1 +Prusa Orange Tough 0.035 = 1 +Prusa Orange Tough 0.05 = 1 +Prusa Orange Tough 0.1 = 1 +Prusa Pink Tough 0.05 = 1 +Prusa Skin Tough 0.05 = 1 +Prusa Transparent Red Tough 0.05 = 1 +Prusa White Tough 0.05 = 1 + # All presets starting with asterisk, for example *common*, are intermediate and they will # not make it into the user interface. @@ -1128,6 +1151,7 @@ filament_density = 3.9 filament_colour = #804040 filament_max_volumetric_speed = 9 filament_notes = "List of materials tested with standard print settings:\n\nColorFabb bronzeFill\nColorFabb brassFill\nColorFabb steelFill\nColorFabb copperFill" +filament_vendor = ColorFabb [filament:ColorFabb HT] inherits = *PET* @@ -1145,11 +1169,13 @@ max_fan_speed = 20 min_fan_speed = 10 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}45{endif}; Filament gcode" temperature = 270 +filament_vendor = ColorFabb [filament:ColorFabb PLA-PHA] inherits = *PLA* filament_cost = 55.5 filament_density = 1.24 +filament_vendor = ColorFabb [filament:ColorFabb woodFill] inherits = *PLA* @@ -1163,6 +1189,7 @@ filament_max_volumetric_speed = 10 first_layer_temperature = 200 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode" temperature = 200 +filament_vendor = ColorFabb [filament:ColorFabb corkFill] inherits = *PLA* @@ -1175,6 +1202,7 @@ filament_max_volumetric_speed = 6 first_layer_temperature = 220 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode" temperature = 220 +filament_vendor = ColorFabb [filament:ColorFabb XT] inherits = *PET* @@ -1184,6 +1212,7 @@ filament_density = 1.27 first_layer_bed_temperature = 90 first_layer_temperature = 260 temperature = 270 +filament_vendor = ColorFabb [filament:ColorFabb XT-CF20] inherits = *PET* @@ -1199,6 +1228,7 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{el temperature = 260 filament_retract_length = nil filament_retract_lift = 0.2 +filament_vendor = ColorFabb [filament:ColorFabb nGen] inherits = *PET* @@ -1211,6 +1241,7 @@ filament_type = NGEN first_layer_temperature = 240 max_fan_speed = 35 min_fan_speed = 20 +filament_vendor = ColorFabb [filament:ColorFabb nGen flex] inherits = *FLEX* @@ -1231,12 +1262,14 @@ temperature = 260 filament_retract_length = nil filament_retract_lift = 0 compatible_printers_condition = nozzle_diameter[0]>0.35 and num_extruders==1 && ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material) +filament_vendor = ColorFabb [filament:E3D Edge] inherits = *PET* filament_cost = 56.9 filament_density = 1.26 filament_type = EDGE +filament_vendor = E3D [filament:E3D PC-ABS] inherits = *ABS* @@ -1245,6 +1278,7 @@ filament_type = PC filament_density = 1.05 first_layer_temperature = 270 temperature = 270 +filament_vendor = E3D [filament:Fillamentum ABS] inherits = *ABS* @@ -1252,6 +1286,7 @@ filament_cost = 32.4 filament_density = 1.04 first_layer_temperature = 240 temperature = 240 +filament_vendor = Fillamentum [filament:Fillamentum ASA] inherits = *ABS* @@ -1266,6 +1301,7 @@ slowdown_below_layer_time = 15 first_layer_temperature = 265 temperature = 265 filament_type = ASA +filament_vendor = Fillamentum [filament:Prusament ASA] inherits = *ABS* @@ -1296,6 +1332,7 @@ first_layer_temperature = 275 max_fan_speed = 50 min_fan_speed = 50 temperature = 275 +filament_vendor = Fillamentum [filament:Fillamentum Timberfill] inherits = *PLA* @@ -1309,24 +1346,28 @@ filament_max_volumetric_speed = 10 first_layer_temperature = 190 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode" temperature = 190 +filament_vendor = Fillamentum [filament:Generic ABS] inherits = *ABS* filament_cost = 27.82 filament_density = 1.04 filament_notes = "List of materials tested with standard ABS print settings:\n\nEsun ABS\nFil-A-Gehr ABS\nHatchboxABS\nPlasty Mladec ABS" +filament_vendor = Generic [filament:Generic PET] inherits = *PET* filament_cost = 27.82 filament_density = 1.27 filament_notes = "List of manufacturers tested with standard PET print settings:\n\nE3D Edge\nFillamentum CPE GH100\nPlasty Mladec PETG" +filament_vendor = Generic [filament:Generic PLA] inherits = *PLA* filament_cost = 25.4 filament_density = 1.24 filament_notes = "List of materials tested with standard PLA print settings:\n\nDas Filament\nEsun PLA\nEUMAKERS PLA\nFiberlogy HD-PLA\nFillamentum PLA\nFloreon3D\nHatchbox PLA\nPlasty Mladec PLA\nPrimavalue PLA\nProto pasta Matte Fiber\nVerbatim PLA\nVerbatim BVOH" +filament_vendor = Generic [filament:Generic FLEX] inherits = *FLEX* @@ -1347,6 +1388,7 @@ filament_colour = #3A80CA first_layer_bed_temperature = 100 first_layer_temperature = 270 temperature = 270 +filament_vendor = Polymaker [filament:PrimaSelect PVA+] inherits = *PLA* @@ -1363,12 +1405,14 @@ filament_type = PVA first_layer_temperature = 195 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode" temperature = 195 +filament_vendor = PrimaSelect [filament:Prusa ABS] inherits = *ABS* filament_cost = 27.82 filament_density = 1.08 filament_notes = "List of materials tested with standard ABS print settings:\n\nEsun ABS\nFil-A-Gehr ABS\nHatchboxABS\nPlasty Mladec ABS" +filament_vendor = Prusa [filament:*ABS MMU2*] inherits = Prusa ABS @@ -1385,6 +1429,7 @@ filament_unloading_speed = 20 [filament:Generic ABS MMU2] inherits = *ABS MMU2* +filament_vendor = Generic [filament:Prusament ASA MMU2] inherits = *ABS MMU2* @@ -1410,6 +1455,7 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{el [filament:Prusa ABS MMU2] inherits = *ABS MMU2* +filament_vendor = Prusa [filament:Prusa HIPS] inherits = *ABS* @@ -1428,6 +1474,7 @@ max_fan_speed = 20 min_fan_speed = 20 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode" temperature = 220 +filament_vendor = Prusa [filament:Prusa PET] inherits = *PET* @@ -1435,6 +1482,7 @@ filament_cost = 27.82 filament_density = 1.27 filament_notes = "List of manufacturers tested with standard PET print settings:\n\nE3D Edge\nPlasty Mladec PETG" compatible_printers_condition = nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +filament_vendor = Prusa [filament:Prusament PETG] inherits = *PET* @@ -1444,12 +1492,14 @@ filament_cost = 24.99 filament_density = 1.27 filament_type = PETG compatible_printers_condition = nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +filament_vendor = Prusa [filament:Prusa PET 0.6 nozzle] inherits = *PET06* filament_cost = 27.82 filament_density = 1.27 filament_notes = "List of manufacturers tested with standard PET print settings:\n\nE3D Edge\nPlasty Mladec PETG" +filament_vendor = Prusa [filament:Prusament PETG 0.6 nozzle] inherits = *PET06* @@ -1458,6 +1508,7 @@ temperature = 250 filament_cost = 24.99 filament_density = 1.27 filament_type = PETG +filament_vendor = Prusa [filament:*PET MMU2*] inherits = Prusa PET @@ -1485,9 +1536,11 @@ filament_max_volumetric_speed = 13 [filament:Generic PET MMU2] inherits = *PET MMU2* +filament_vendor = Generic [filament:Prusa PET MMU2] inherits = *PET MMU2* +filament_vendor = Prusa [filament:Prusament PETG MMU2] inherits = *PET MMU2* @@ -1498,16 +1551,19 @@ inherits = *PET MMU2 06* [filament:Prusa PET MMU2 0.6 nozzle] inherits = *PET MMU2 06* +filament_vendor = Prusa [filament:Prusament PETG MMU2 0.6 nozzle] inherits = *PET MMU2 06* filament_type = PETG +filament_vendor = Prusa [filament:Prusa PLA] inherits = *PLA* filament_cost = 25.4 filament_density = 1.24 filament_notes = "List of materials tested with standard PLA print settings:\n\nDas Filament\nEsun PLA\nEUMAKERS PLA\nFiberlogy HD-PLA\nFiberlogy PLA\nFillamentum PLA\nFloreon3D\nHatchbox PLA\nPlasty Mladec PLA\nPrimavalue PLA\nProto pasta Matte Fiber\nVerbatim PLA\nAmazonBasics PLA" +filament_vendor = Prusa [filament:Prusament PLA] inherits = *PLA* @@ -1515,6 +1571,7 @@ temperature = 215 filament_cost = 24.99 filament_density = 1.24 filament_notes = "Affordable filament for everyday printing in premium quality manufactured in-house by Josef Prusa" +filament_vendor = Prusa [filament:*PLA MMU2*] inherits = Prusa PLA @@ -1534,18 +1591,22 @@ filament_unloading_speed_start = 100 [filament:Generic PLA MMU2] inherits = *PLA MMU2* +filament_vendor = Generic [filament:Prusa PLA MMU2] inherits = *PLA MMU2* +filament_vendor = Prusa [filament:Prusament PLA MMU2] inherits = *PLA MMU2* +filament_vendor = Prusa [filament:SemiFlex or Flexfill 98A] inherits = *FLEX* filament_cost = 82 filament_density = 1.22 filament_max_volumetric_speed = 1.35 +filament_vendor = Flexfill [filament:Taulman Bridge] inherits = *common* @@ -1567,6 +1628,7 @@ max_fan_speed = 5 min_fan_speed = 0 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode" temperature = 250 +filament_vendor = Taulman [filament:Taulman T-Glase] inherits = *PET* @@ -1580,6 +1642,7 @@ first_layer_temperature = 240 max_fan_speed = 5 min_fan_speed = 0 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}30{endif}; Filament gcode" +filament_vendor = Taulman [filament:Verbatim BVOH] inherits = *common* @@ -1603,6 +1666,7 @@ max_fan_speed = 100 min_fan_speed = 100 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode" temperature = 210 +filament_vendor = Verbatim [filament:Verbatim BVOH MMU2] inherits = Verbatim BVOH @@ -1622,6 +1686,7 @@ filament_unload_time = 12 filament_unloading_speed = 20 filament_unloading_speed_start = 100 filament_loading_speed_start = 19 +filament_vendor = Verbatim [filament:PrimaSelect PVA+ MMU2] inherits = *common* @@ -1660,6 +1725,7 @@ min_print_speed = 15 slowdown_below_layer_time = 20 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}30{endif}; Filament gcode" temperature = 195 +filament_vendor = PrimaSelect [filament:Verbatim PP] inherits = *common* @@ -1682,6 +1748,7 @@ max_fan_speed = 100 min_fan_speed = 100 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode" temperature = 220 +filament_vendor = Verbatim ## Filaments MMU1 @@ -1899,9 +1966,11 @@ exposure_time = 6 initial_exposure_time = 40 [sla_material:BlueCast Keramaster Dental 0.025] +material_type = Dental inherits = *common 0.025* exposure_time = 6 initial_exposure_time = 45 +material_vendor = Bluecast [sla_material:BlueCast X10 0.025] inherits = *common 0.025* @@ -1912,6 +1981,7 @@ initial_exposure_time = 100 inherits = *common 0.025* exposure_time = 6 initial_exposure_time = 35 +material_vendor = Prusa [sla_material:Prusa Grey Tough 0.025] inherits = *common 0.025* @@ -1964,31 +2034,38 @@ initial_exposure_time = 35 inherits = *common 0.05* exposure_time = 7 initial_exposure_time = 35 +material_vendor = Bluecast [sla_material:BlueCast Keramaster 0.05] inherits = *common 0.05* exposure_time = 8 initial_exposure_time = 45 +material_vendor = Bluecast [sla_material:BlueCast Keramaster Dental 0.05] +material_type = Dental inherits = *common 0.05* exposure_time = 7 initial_exposure_time = 50 +material_vendor = Bluecast [sla_material:BlueCast LCD-DLP Original 0.05] inherits = *common 0.05* exposure_time = 10 initial_exposure_time = 60 +material_vendor = Bluecast [sla_material:BlueCast Phrozen Wax 0.05] inherits = *common 0.05* exposure_time = 16 initial_exposure_time = 50 +material_vendor = Bluecast [sla_material:BlueCast S+ 0.05] inherits = *common 0.05* exposure_time = 9 initial_exposure_time = 45 +material_vendor = Bluecast [sla_material:BlueCast X10 0.05] inherits = *common 0.05* @@ -1999,26 +2076,31 @@ initial_exposure_time = 100 inherits = *common 0.05* exposure_time = 6 initial_exposure_time = 40 +material_vendor = Monocure [sla_material:Monocure 3D Blue Rapid Resin 0.05] inherits = *common 0.05* exposure_time = 7 initial_exposure_time = 40 +material_vendor = Monocure [sla_material:Monocure 3D Clear Rapid Resin 0.05] inherits = *common 0.05* exposure_time = 8 initial_exposure_time = 40 +material_vendor = Monocure [sla_material:Monocure 3D Grey Rapid Resin 0.05] inherits = *common 0.05* exposure_time = 10 initial_exposure_time = 30 +material_vendor = Monocure [sla_material:Monocure 3D White Rapid Resin 0.05] inherits = *common 0.05* exposure_time = 7 initial_exposure_time = 40 +material_vendor = Monocure [sla_material:3DM-HTR140 (high temperature) 0.05] inherits = *common 0.05* @@ -2034,36 +2116,43 @@ initial_exposure_time = 25 inherits = *common 0.05* exposure_time = 20 initial_exposure_time = 40 +material_vendor = 3DM [sla_material:3DM-DENT 0.05] inherits = *common 0.05* exposure_time = 7 initial_exposure_time = 45 +material_vendor = 3DM [sla_material:3DM-HR Green 0.05] inherits = *common 0.05* exposure_time = 15 initial_exposure_time = 40 +material_vendor = 3DM [sla_material:3DM-HR Red Wine 0.05] inherits = *common 0.05* exposure_time = 9 initial_exposure_time = 35 +material_vendor = 3DM [sla_material:3DM-XPRO White 0.05] inherits = *common 0.05* exposure_time = 9 initial_exposure_time = 35 +material_vendor = 3DM [sla_material:FTD Ash Grey 0.05] inherits = *common 0.05* exposure_time = 9 initial_exposure_time = 40 +material_vendor = FTD [sla_material:Harz Labs Model Resin Cherry 0.05] inherits = *common 0.05* exposure_time = 8 initial_exposure_time = 45 +material_vendor = Harz Labs [sla_material:Photocentric Hard Grey 0.05] inherits = *common 0.05* @@ -2116,6 +2205,7 @@ initial_exposure_time = 35 inherits = *common 0.05* exposure_time = 13 initial_exposure_time = 40 +material_vendor = Prusa ## [sla_material:Prusa Yellow Solid 0.05] ## inherits = *common 0.05* @@ -2126,6 +2216,7 @@ initial_exposure_time = 40 inherits = *common 0.05* exposure_time = 7.5 initial_exposure_time = 35 +material_vendor = Prusa ## [sla_material:Prusa Transparent Green Tough 0.05] ## inherits = *common 0.05* @@ -2136,21 +2227,25 @@ initial_exposure_time = 35 inherits = *common 0.05* exposure_time = 6 initial_exposure_time = 35 +material_vendor = Prusa [sla_material:Prusa Maroon Tough 0.05] inherits = *common 0.05* exposure_time = 7.5 initial_exposure_time = 35 +material_vendor = Prusa [sla_material:Prusa Pink Tough 0.05] inherits = *common 0.05* exposure_time = 8 initial_exposure_time = 35 +material_vendor = Prusa [sla_material:Prusa Azure Blue Tough 0.05] inherits = *common 0.05* exposure_time = 8 initial_exposure_time = 35 +material_vendor = Prusa [sla_material:Prusa Transparent Tough 0.05] inherits = *common 0.05* @@ -2193,6 +2288,7 @@ initial_exposure_time = 15 inherits = *common 0.035* exposure_time = 6 initial_exposure_time = 35 +material_vendor = Prusa ########### Materials 0.1 @@ -2235,6 +2331,7 @@ initial_exposure_time = 55 inherits = *common 0.1* exposure_time = 8 initial_exposure_time = 35 +material_vendor = Prusa [sla_material:Prusa Green Casting 0.1] inherits = *common 0.1* diff --git a/src/libnest2d/include/libnest2d.h b/src/libnest2d/include/libnest2d.h index 4661b45744..76b133f4b4 100644 --- a/src/libnest2d/include/libnest2d.h +++ b/src/libnest2d/include/libnest2d.h @@ -49,91 +49,84 @@ using BottomLeftPlacer = placers::_BottomLeftPlacer; extern template class Nester; extern template class Nester; -extern template PackGroup Nester::execute( +extern template std::size_t Nester::execute( std::vector::iterator, std::vector::iterator); -extern template PackGroup Nester::execute( +extern template std::size_t Nester::execute( std::vector::iterator, std::vector::iterator); #endif -template::iterator> -void nest(Iterator from, Iterator to, - const typename Placer::BinType& bin, - Coord dist = 0, - const typename Placer::Config& pconf = {}, - const typename Selector::Config& sconf = {}) -{ - _Nester nester(bin, dist, pconf, sconf); - nester.execute(from, to); -} +template +struct NestConfig { + typename Placer::Config placer_config; + typename Selector::Config selector_config; + using Placement = typename Placer::Config; + using Selection = typename Selector::Config; + + NestConfig() = default; + NestConfig(const typename Placer::Config &cfg) : placer_config{cfg} {} + NestConfig(const typename Selector::Config &cfg) : selector_config{cfg} {} + NestConfig(const typename Placer::Config & pcfg, + const typename Selector::Config &scfg) + : placer_config{pcfg}, selector_config{scfg} {} +}; + +struct NestControl { + ProgressFunction progressfn; + StopCondition stopcond = []{ return false; }; + + NestControl() = default; + NestControl(ProgressFunction pr) : progressfn{std::move(pr)} {} + NestControl(StopCondition sc) : stopcond{std::move(sc)} {} + NestControl(ProgressFunction pr, StopCondition sc) + : progressfn{std::move(pr)}, stopcond{std::move(sc)} + {} +}; template::iterator> -void nest(Iterator from, Iterator to, - const typename Placer::BinType& bin, - ProgressFunction prg, - StopCondition scond = []() { return false; }, - Coord dist = 0, - const typename Placer::Config& pconf = {}, - const typename Selector::Config& sconf = {}) +std::size_t nest(Iterator from, Iterator to, + const typename Placer::BinType & bin, + Coord dist = 0, + const NestConfig &cfg = {}, + NestControl ctl = {}) { - _Nester nester(bin, dist, pconf, sconf); - if(prg) nester.progressIndicator(prg); - if(scond) nester.stopCondition(scond); - nester.execute(from, to); + _Nester nester{bin, dist, cfg.placer_config, cfg.selector_config}; + if(ctl.progressfn) nester.progressIndicator(ctl.progressfn); + if(ctl.stopcond) nester.stopCondition(ctl.stopcond); + return nester.execute(from, to); } #ifdef LIBNEST2D_STATIC extern template class Nester; extern template class Nester; - -extern template void nest(std::vector::iterator from, - std::vector::iterator to, - const Box& bin, - Coord dist = 0, - const NfpPlacer::Config& pconf, - const FirstFitSelection::Config& sconf); - -extern template void nest(std::vector::iterator from, - std::vector::iterator to, - const Box& bin, - ProgressFunction prg, - StopCondition scond, - Coord dist = 0, - const NfpPlacer::Config& pconf, - const FirstFitSelection::Config& sconf); +extern template std::size_t nest(std::vector::iterator from, + std::vector::iterator from to, + const Box & bin, + Coord dist, + const NestConfig &cfg, + NestControl ctl); +extern template std::size_t nest(std::vector::iterator from, + std::vector::iterator from to, + const Box & bin, + Coord dist, + const NestConfig &cfg, + NestControl ctl); #endif template> -void nest(Container&& cont, - const typename Placer::BinType& bin, - Coord dist = 0, - const typename Placer::Config& pconf = {}, - const typename Selector::Config& sconf = {}) +std::size_t nest(Container&& cont, + const typename Placer::BinType & bin, + Coord dist = 0, + const NestConfig &cfg = {}, + NestControl ctl = {}) { - nest(cont.begin(), cont.end(), bin, dist, pconf, sconf); -} - -template> -void nest(Container&& cont, - const typename Placer::BinType& bin, - ProgressFunction prg, - StopCondition scond = []() { return false; }, - Coord dist = 0, - const typename Placer::Config& pconf = {}, - const typename Selector::Config& sconf = {}) -{ - nest(cont.begin(), cont.end(), bin, prg, scond, dist, - pconf, sconf); + return nest(cont.begin(), cont.end(), bin, dist, cfg, ctl); } } diff --git a/src/libnest2d/include/libnest2d/libnest2d.hpp b/src/libnest2d/include/libnest2d/libnest2d.hpp index 29d52c10f8..91c98c62a7 100644 --- a/src/libnest2d/include/libnest2d/libnest2d.hpp +++ b/src/libnest2d/include/libnest2d/libnest2d.hpp @@ -129,8 +129,12 @@ public: sh_(sl::create(std::move(contour), std::move(holes))) {} inline bool isFixed() const noexcept { return fixed_; } - inline void markAsFixed(bool fixed = true) { fixed_ = fixed; } - + inline void markAsFixedInBin(int binid) + { + fixed_ = binid >= 0; + binid_ = binid; + } + inline void binId(int idx) { binid_ = idx; } inline int binId() const noexcept { return binid_; } @@ -748,6 +752,7 @@ template class _Nester { using TSel = SelectionStrategyLike; TSel selector_; + public: using Item = typename PlacementStrategy::Item; using ShapeType = typename Item::ShapeType; @@ -824,7 +829,7 @@ public: * the selection algorithm. */ template - inline ItemIteratorOnly execute(It from, It to) + inline ItemIteratorOnly execute(It from, It to) { auto infl = static_cast(std::ceil(min_obj_distance_/2.0)); if(infl > 0) std::for_each(from, to, [this, infl](Item& item) { @@ -837,6 +842,8 @@ public: if(min_obj_distance_ > 0) std::for_each(from, to, [infl](Item& item) { item.inflate(-infl); }); + + return selector_.getResult().size(); } /// Set a progress indicator function object for the selector. diff --git a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp index 686857a87f..0030287586 100644 --- a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp +++ b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp @@ -1122,8 +1122,6 @@ private: sl::rotate(sh, item.rotation()); Box bb = sl::boundingBox(sh); - bb.minCorner() += item.translation(); - bb.maxCorner() += item.translation(); Vertex ci, cb; auto bbin = sl::boundingBox(bin_); diff --git a/src/libnest2d/src/libnest2d.cpp b/src/libnest2d/src/libnest2d.cpp index 0214587874..740f6e18d9 100644 --- a/src/libnest2d/src/libnest2d.cpp +++ b/src/libnest2d/src/libnest2d.cpp @@ -5,19 +5,17 @@ namespace libnest2d { template class Nester; template class Nester; -template PackGroup nest(std::vector::iterator from, - std::vector::iterator to, - const Box& bin, - Coord dist = 0, - const NfpPlacer::Config& pconf, - const FirstFitSelection::Config& sconf); +template std::size_t nest(std::vector::iterator from, + std::vector::iterator from to, + const Box & bin, + Coord dist, + const NestConfig &cfg, + NestControl ctl); -template PackGroup nest(std::vector::iterator from, - std::vector::iterator to, - const Box& bin, - ProgressFunction prg, - StopCondition scond, - Coord dist = 0, - const NfpPlacer::Config& pconf, - const FirstFitSelection::Config& sconf); +template std::size_t nest(std::vector::iterator from, + std::vector::iterator from to, + const Box & bin, + Coord dist, + const NestConfig &cfg, + NestControl ctl); } diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index 20dfd8926f..aed6e41f73 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -375,7 +375,7 @@ public: for(unsigned idx = 0; idx < fixeditems.size(); ++idx) { Item& itm = fixeditems[idx]; - itm.markAsFixed(); + itm.markAsFixedInBin(0); } m_pck.configure(m_pconf); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 148ffe1413..b3957218df 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -749,6 +749,10 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionStrings { "" }); def->cli = ConfigOptionDef::nocli; + def = this->add("filament_vendor", coString); + def->set_default_value(new ConfigOptionString(L("(Unknown)"))); + def->cli = ConfigOptionDef::nocli; + def = this->add("fill_angle", coFloat); def->label = L("Fill angle"); def->category = L("Infill"); @@ -2398,6 +2402,18 @@ void PrintConfigDef::init_sla_params() // SLA Material settings. + def = this->add("material_type", coString); + def->label = L("SLA material type"); + def->tooltip = L("SLA material type"); + def->gui_type = "f_enum_open"; // TODO: ??? + def->gui_flags = "show_value"; + def->enum_values.push_back("Tough"); + def->enum_values.push_back("Flexible"); + def->enum_values.push_back("Casting"); + def->enum_values.push_back("Dental"); + def->enum_values.push_back("Heat-resistant"); + def->set_default_value(new ConfigOptionString("Tough")); + def = this->add("initial_layer_height", coFloat); def->label = L("Initial layer height"); def->tooltip = L("Initial layer height"); @@ -2475,6 +2491,10 @@ void PrintConfigDef::init_sla_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionString("")); + def = this->add("material_vendor", coString); + def->set_default_value(new ConfigOptionString(L("(Unknown)"))); + def->cli = ConfigOptionDef::nocli; + def = this->add("default_sla_material_profile", coString); def->label = L("Default SLA material profile"); def->tooltip = L("Default print profile associated with the current printer profile. " diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index e4ffe91992..372e70e349 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -52,6 +52,14 @@ enum FilamentType { }; */ +enum SLAMaterial { + slamTough, + slamFlex, + slamCasting, + slamDental, + slamHeatResistant, +}; + enum SLADisplayOrientation { sladoLandscape, sladoPortrait diff --git a/src/libslic3r/Time.hpp b/src/libslic3r/Time.hpp index 6b3fd1525d..c03251986b 100644 --- a/src/libslic3r/Time.hpp +++ b/src/libslic3r/Time.hpp @@ -46,7 +46,7 @@ time_t str2time(const std::string &str, TimeZone zone, TimeFormat fmt); inline std::string iso_utc_timestamp(time_t t) { - return time2str(t, TimeZone::utc, TimeFormat::gcode); + return time2str(t, TimeZone::utc, TimeFormat::iso8601Z); } inline std::string iso_utc_timestamp() diff --git a/src/slic3r/Config/Snapshot.cpp b/src/slic3r/Config/Snapshot.cpp index 7f157fb1a3..fa756f49da 100644 --- a/src/slic3r/Config/Snapshot.cpp +++ b/src/slic3r/Config/Snapshot.cpp @@ -393,9 +393,9 @@ const Snapshot& SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot: // Read the active config bundle, parse the config version. PresetBundle bundle; bundle.load_configbundle((data_dir / "vendor" / (cfg.name + ".ini")).string(), PresetBundle::LOAD_CFGBUNDLE_VENDOR_ONLY); - for (const VendorProfile &vp : bundle.vendors) - if (vp.id == cfg.name) - cfg.version.config_version = vp.config_version; + for (const auto &vp : bundle.vendors) + if (vp.second.id == cfg.name) + cfg.version.config_version = vp.second.config_version; // Fill-in the min/max slic3r version from the config index, if possible. try { // Load the config index for the vendor. diff --git a/src/slic3r/GUI/AppConfig.cpp b/src/slic3r/GUI/AppConfig.cpp index 6d1c9cddd5..6d0c83393a 100644 --- a/src/slic3r/GUI/AppConfig.cpp +++ b/src/slic3r/GUI/AppConfig.cpp @@ -28,6 +28,9 @@ static const std::string VENDOR_PREFIX = "vendor:"; static const std::string MODEL_PREFIX = "model:"; static const std::string VERSION_CHECK_URL = "https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaSlicer.version"; +const std::string AppConfig::SECTION_FILAMENTS = "filaments"; +const std::string AppConfig::SECTION_MATERIALS = "sla_materials"; + void AppConfig::reset() { m_storage.clear(); diff --git a/src/slic3r/GUI/AppConfig.hpp b/src/slic3r/GUI/AppConfig.hpp index 4959b5433a..1196404a2f 100644 --- a/src/slic3r/GUI/AppConfig.hpp +++ b/src/slic3r/GUI/AppConfig.hpp @@ -80,6 +80,12 @@ public: } } + bool has_section(const std::string §ion) const + { return m_storage.find(section) != m_storage.end(); } + const std::map& get_section(const std::string §ion) const + { return m_storage.find(section)->second; } + void set_section(const std::string §ion, const std::map& data) + { m_storage[section] = data; } void clear_section(const std::string §ion) { m_storage[section].clear(); } @@ -131,6 +137,9 @@ public: bool get_mouse_device_rotation_speed(const std::string& name, float& rotation_speed); #endif // ENABLE_3DCONNEXION_DEVICES + static const std::string SECTION_FILAMENTS; + static const std::string SECTION_MATERIALS; + private: // Map of section, name -> value std::map> m_storage; diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 4e7aff0280..bc73e32625 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -1,9 +1,12 @@ +// FIXME: extract absolute units -> em + #include "ConfigWizard_private.hpp" #include #include #include #include +#include #include #include #include @@ -19,10 +22,10 @@ #include #include #include +#include #include #include "libslic3r/Utils.hpp" -#include "PresetBundle.hpp" #include "GUI.hpp" #include "GUI_Utils.hpp" #include "slic3r/Config/Snapshot.hpp" @@ -37,6 +40,83 @@ using Config::Snapshot; using Config::SnapshotDB; +// Configuration data structures extensions needed for the wizard + +Bundle::Bundle(fs::path source_path, bool is_in_resources, bool is_prusa_bundle) + : preset_bundle(new PresetBundle) + , vendor_profile(nullptr) + , is_in_resources(is_in_resources) + , is_prusa_bundle(is_prusa_bundle) +{ + preset_bundle->load_configbundle(source_path.string(), PresetBundle::LOAD_CFGBNDLE_SYSTEM); + auto first_vendor = preset_bundle->vendors.begin(); + wxCHECK_RET(first_vendor != preset_bundle->vendors.end(), "Failed to load preset bundle"); + vendor_profile = &first_vendor->second; +} + +Bundle::Bundle(Bundle &&other) + : preset_bundle(std::move(other.preset_bundle)) + , vendor_profile(other.vendor_profile) + , is_in_resources(other.is_in_resources) + , is_prusa_bundle(other.is_prusa_bundle) +{ + other.vendor_profile = nullptr; +} + +BundleMap BundleMap::load() +{ + BundleMap res; + + const auto vendor_dir = (boost::filesystem::path(Slic3r::data_dir()) / "vendor").make_preferred(); + const auto rsrc_vendor_dir = (boost::filesystem::path(resources_dir()) / "profiles").make_preferred(); + + auto prusa_bundle_path = (vendor_dir / PresetBundle::PRUSA_BUNDLE).replace_extension(".ini"); + auto prusa_bundle_rsrc = false; + if (! boost::filesystem::exists(prusa_bundle_path)) { + prusa_bundle_path = (rsrc_vendor_dir / PresetBundle::PRUSA_BUNDLE).replace_extension(".ini"); + prusa_bundle_rsrc = true; + } + Bundle prusa_bundle(std::move(prusa_bundle_path), prusa_bundle_rsrc, true); + res.emplace(PresetBundle::PRUSA_BUNDLE, std::move(prusa_bundle)); + + // Load the other bundles in the datadir/vendor directory + // and then additionally from resources/profiles. + bool is_in_resources = false; + for (auto dir : { &vendor_dir, &rsrc_vendor_dir }) { + for (const auto &dir_entry : boost::filesystem::directory_iterator(*dir)) { + if (Slic3r::is_ini_file(dir_entry)) { + std::string id = dir_entry.path().stem().string(); // stem() = filename() without the trailing ".ini" part + + // Don't load this bundle if we've already loaded it. + if (res.find(id) != res.end()) { continue; } + + Bundle bundle(dir_entry.path(), is_in_resources); + res.emplace(std::move(id), std::move(bundle)); + } + } + + is_in_resources = true; + } + + return res; +} + +Bundle& BundleMap::prusa_bundle() +{ + auto it = find(PresetBundle::PRUSA_BUNDLE); + if (it == end()) { + throw std::runtime_error("ConfigWizard: Internal error in BundleMap: PRUSA_BUNDLE not loaded"); + } + + return it->second; +} + +const Bundle& BundleMap::prusa_bundle() const +{ + return const_cast(this)->prusa_bundle(); +} + + // Printer model picker GUI control struct PrinterPickerEvent : public wxEvent @@ -62,7 +142,9 @@ struct PrinterPickerEvent : public wxEvent wxDEFINE_EVENT(EVT_PRINTER_PICK, PrinterPickerEvent); -PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig_vendors, const ModelFilter &filter) +const std::string PrinterPicker::PRINTER_PLACEHOLDER = "printer_placeholder.png"; + +PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig, const ModelFilter &filter) : wxPanel(parent) , vendor_id(vendor.id) , width(0) @@ -93,6 +175,17 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxSt if (wxFileExists(bitmap_file)) { bitmap.LoadFile(bitmap_file, wxBITMAP_TYPE_PNG); bitmap_width = bitmap.GetWidth(); + } else { + BOOST_LOG_TRIVIAL(warning) << boost::format("Can't find bitmap file `%1%` for vendor `%2%`, printer `%3%`, using placeholder icon instead") + % bitmap_file + % vendor.id + % model.id; + + const wxString placeholder_file = GUI::from_u8(Slic3r::var(PRINTER_PLACEHOLDER)); + if (wxFileExists(placeholder_file)) { + bitmap.LoadFile(placeholder_file, wxBITMAP_TYPE_PNG); + bitmap_width = bitmap.GetWidth(); + } } auto *title = new wxStaticText(this, wxID_ANY, model.name, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); @@ -132,7 +225,7 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxSt auto *cbox = new Checkbox(variants_panel, label, model_id, variant.name); i == 0 ? cboxes.push_back(cbox) : cboxes_alt.push_back(cbox); - bool enabled = appconfig_vendors.get_variant("PrusaResearch", model_id, variant.name); + const bool enabled = appconfig.get_variant(vendor.id, model_id, variant.name); cbox->SetValue(enabled); variants_sizer->Add(cbox, 0, wxBOTTOM, 3); @@ -210,8 +303,8 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxSt SetSizer(sizer); } -PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig_vendors) - : PrinterPicker(parent, vendor, std::move(title), max_cols, appconfig_vendors, [](const VendorProfile::PrinterModel&) { return true; }) +PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig) + : PrinterPicker(parent, vendor, std::move(title), max_cols, appconfig, [](const VendorProfile::PrinterModel&) { return true; }) {} void PrinterPicker::select_all(bool select, bool alternates) @@ -241,6 +334,19 @@ void PrinterPicker::select_one(size_t i, bool select) } } +bool PrinterPicker::any_selected() const +{ + for (const auto &cb : cboxes) { + if (cb->GetValue()) { return true; } + } + + for (const auto &cb : cboxes_alt) { + if (cb->GetValue()) { return true; } + } + + return false; +} + void PrinterPicker::on_checkbox(const Checkbox *cbox, bool checked) { PrinterPickerEvent evt(EVT_PRINTER_PICK, GetId(), vendor_id, cbox->model, cbox->variant, checked); @@ -279,16 +385,18 @@ ConfigWizardPage::ConfigWizardPage(ConfigWizard *parent, wxString title, wxStrin ConfigWizardPage::~ConfigWizardPage() {} -void ConfigWizardPage::append_text(wxString text) +wxStaticText* ConfigWizardPage::append_text(wxString text) { auto *widget = new wxStaticText(this, wxID_ANY, text, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); widget->Wrap(WRAP_WIDTH); widget->SetMinSize(wxSize(WRAP_WIDTH, -1)); append(widget); + return widget; } void ConfigWizardPage::append_spacer(int space) { + // FIXME: scaling content->AddSpacer(space); } @@ -303,34 +411,42 @@ PageWelcome::PageWelcome(ConfigWizard *parent) _(L("Welcome to the %s Configuration Wizard")) #endif , SLIC3R_APP_NAME), _(L("Welcome"))) - , cbox_reset(nullptr) + , welcome_text(append_text(wxString::Format( + _(L("Hello, welcome to %s! This %s helps you with the initial configuration; just a few settings and you will be ready to print.")), + SLIC3R_APP_NAME, + ConfigWizard::name()) + )) + , cbox_reset(append( + new wxCheckBox(this, wxID_ANY, _(L("Remove user profiles - install from scratch (a snapshot will be taken beforehand)"))) + )) { - if (wizard_p()->run_reason == ConfigWizard::RR_DATA_EMPTY) { - wxString::Format(_(L("Run %s")), ConfigWizard::name()); - append_text(wxString::Format( - _(L("Hello, welcome to %s! This %s helps you with the initial configuration; just a few settings and you will be ready to print.")), - SLIC3R_APP_NAME, - ConfigWizard::name()) - ); - } else { - cbox_reset = new wxCheckBox(this, wxID_ANY, _(L("Remove user profiles - install from scratch (a snapshot will be taken beforehand)"))); - append(cbox_reset); - } + welcome_text->Hide(); + cbox_reset->Hide(); +} - Show(); +void PageWelcome::set_run_reason(ConfigWizard::RunReason run_reason) +{ + const bool data_empty = run_reason == ConfigWizard::RR_DATA_EMPTY; + welcome_text->Show(data_empty); + cbox_reset->Show(!data_empty); } -PagePrinters::PagePrinters(ConfigWizard *parent, wxString title, wxString shortname, const VendorProfile &vendor, unsigned indent, Technology technology) +PagePrinters::PagePrinters(ConfigWizard *parent, + wxString title, + wxString shortname, + const VendorProfile &vendor, + unsigned indent, + Technology technology) : ConfigWizardPage(parent, std::move(title), std::move(shortname), indent) + , technology(technology) + , install(false) // only used for 3rd party vendors { enum { COL_SIZE = 200, }; - bool check_first_variant = technology == T_FFF && wizard_p()->check_first_variant(); - - AppConfig &appconfig_vendors = this->wizard_p()->appconfig_vendors; + AppConfig *appconfig = &this->wizard_p()->appconfig_new; const auto families = vendor.families(); for (const auto &family : families) { @@ -345,16 +461,11 @@ PagePrinters::PagePrinters(ConfigWizard *parent, wxString title, wxString shortn } const auto picker_title = family.empty() ? wxString() : wxString::Format(_(L("%s Family")), family); - auto *picker = new PrinterPicker(this, vendor, picker_title, MAX_COLS, appconfig_vendors, filter); + auto *picker = new PrinterPicker(this, vendor, picker_title, MAX_COLS, *appconfig, filter); - if (check_first_variant) { - // Select the default (first) model/variant on the Prusa vendor - picker->select_one(0, true); - check_first_variant = false; - } - - picker->Bind(EVT_PRINTER_PICK, [this, &appconfig_vendors](const PrinterPickerEvent &evt) { - appconfig_vendors.set_variant(evt.vendor_id, evt.model_id, evt.variant_name, evt.enable); + picker->Bind(EVT_PRINTER_PICK, [this, appconfig](const PrinterPickerEvent &evt) { + appconfig->set_variant(evt.vendor_id, evt.model_id, evt.variant_name, evt.enable); + wizard_p()->on_printer_pick(this, evt); }); append(new wxStaticLine(this)); @@ -377,6 +488,195 @@ int PagePrinters::get_width() const [](int acc, const PrinterPicker *picker) { return std::max(acc, picker->get_width()); }); } +bool PagePrinters::any_selected() const +{ + for (const auto *picker : printer_pickers) { + if (picker->any_selected()) { return true; } + } + + return false; +} + +void PagePrinters::set_run_reason(ConfigWizard::RunReason run_reason) +{ + if (technology == T_FFF + && (run_reason == ConfigWizard::RR_DATA_EMPTY || run_reason == ConfigWizard::RR_DATA_LEGACY) + && printer_pickers.size() > 0) { + printer_pickers[0]->select_one(0, true); + } +} + + +const std::string PageMaterials::EMPTY; + +PageMaterials::PageMaterials(ConfigWizard *parent, Materials *materials, wxString title, wxString shortname, wxString list1name) + : ConfigWizardPage(parent, std::move(title), std::move(shortname)) + , materials(materials) + , list_l1(new StringList(this)) + , list_l2(new StringList(this)) + , list_l3(new PresetList(this)) +{ + append_spacer(VERTICAL_SPACING); + + const int em = parent->em_unit(); + const int list_h = 30*em; + + list_l1->SetMinSize(wxSize(8*em, list_h)); + list_l2->SetMinSize(wxSize(13*em, list_h)); + list_l3->SetMinSize(wxSize(25*em, list_h)); + + auto *grid = new wxFlexGridSizer(3, 0, em); + + grid->Add(new wxStaticText(this, wxID_ANY, list1name)); + grid->Add(new wxStaticText(this, wxID_ANY, _(L("Vendor:")))); + grid->Add(new wxStaticText(this, wxID_ANY, _(L("Profile:")))); + + grid->Add(list_l1); + grid->Add(list_l2); + grid->Add(list_l3); + + auto *btn_sizer = new wxBoxSizer(wxHORIZONTAL); + auto *sel_all = new wxButton(this, wxID_ANY, _(L("All"))); + auto *sel_none = new wxButton(this, wxID_ANY, _(L("None"))); + btn_sizer->Add(sel_all, 0, wxRIGHT, em / 2); + btn_sizer->Add(sel_none); + + grid->Add(new wxBoxSizer(wxHORIZONTAL)); + grid->Add(new wxBoxSizer(wxHORIZONTAL)); + grid->Add(btn_sizer, 0, wxALIGN_RIGHT); + + append(grid); + + list_l1->Bind(wxEVT_LISTBOX, [this](wxCommandEvent &) { + update_lists(list_l1->GetSelection(), list_l2->GetSelection()); + }); + list_l2->Bind(wxEVT_LISTBOX, [this](wxCommandEvent &) { + update_lists(list_l1->GetSelection(), list_l2->GetSelection()); + }); + + list_l3->Bind(wxEVT_CHECKLISTBOX, [this](wxCommandEvent &evt) { select_material(evt.GetInt()); }); + + sel_all->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { select_all(true); }); + sel_none->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { select_all(false); }); + + reload_presets(); +} + +void PageMaterials::reload_presets() +{ + clear(); + + list_l1->append(_(L("(All)")), &EMPTY); + + for (const std::string &type : materials->types) { + list_l1->append(type, &type); + } + + if (list_l1->GetCount() > 0) { + list_l1->SetSelection(0); + sel1_prev = wxNOT_FOUND; + sel2_prev = wxNOT_FOUND; + update_lists(0, 0); + } + + presets_loaded = true; +} + +void PageMaterials::update_lists(int sel1, int sel2) +{ + wxWindowUpdateLocker freeze_guard(this); + (void)freeze_guard; + + if (sel1 != sel1_prev) { + // Refresh the second list + + // XXX: The vendor list is created with quadratic complexity here, + // but the number of vendors is going to be very small this shouldn't be a problem. + + list_l2->Clear(); + list_l2->append(_(L("(All)")), &EMPTY); + if (sel1 != wxNOT_FOUND) { + const std::string &type = list_l1->get_data(sel1); + + materials->filter_presets(type, EMPTY, [this](const Preset *p) { + const std::string &vendor = this->materials->get_vendor(p); + + if (list_l2->find(vendor) == wxNOT_FOUND) { + list_l2->append(vendor, &vendor); + } + }); + } + + sel1_prev = sel1; + sel2 = 0; + sel2_prev = wxNOT_FOUND; + list_l2->SetSelection(sel2); + list_l3->Clear(); + } + + if (sel2 != sel2_prev) { + // Refresh the third list + + list_l3->Clear(); + if (sel1 != wxNOT_FOUND && sel2 != wxNOT_FOUND) { + const std::string &type = list_l1->get_data(sel1); + const std::string &vendor = list_l2->get_data(sel2); + + materials->filter_presets(type, vendor, [this](const Preset *p) { + const int i = list_l3->append(p->name, p); + const bool checked = wizard_p()->appconfig_new.has(materials->appconfig_section(), p->name); + list_l3->Check(i, checked); + }); + } + + sel2_prev = sel2; + } +} + +void PageMaterials::select_material(int i) +{ + const bool checked = list_l3->IsChecked(i); + const Preset &preset = list_l3->get_data(i); + + if (checked) { + wizard_p()->appconfig_new.set(materials->appconfig_section(), preset.name, "1"); + } else { + wizard_p()->appconfig_new.erase(materials->appconfig_section(), preset.name); + } +} + +void PageMaterials::select_all(bool select) +{ + wxWindowUpdateLocker freeze_guard(this); + (void)freeze_guard; + + for (unsigned i = 0; i < list_l3->GetCount(); i++) { + const bool current = list_l3->IsChecked(i); + if (current != select) { + list_l3->Check(i, select); + select_material(i); + } + } +} + +void PageMaterials::clear() +{ + list_l1->Clear(); + list_l2->Clear(); + list_l3->Clear(); + sel1_prev = wxNOT_FOUND; + sel2_prev = wxNOT_FOUND; + presets_loaded = false; +} + +void PageMaterials::on_activate() +{ + if (! presets_loaded) { + wizard_p()->update_materials(materials->technology); + reload_presets(); + } +} + const char *PageCustom::default_profile_name = "My Settings"; @@ -400,7 +700,7 @@ PageCustom::PageCustom(ConfigWizard *parent) cb_custom->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { tc_profile_name->Enable(custom_wanted()); - wizard_p()->on_custom_setup(custom_wanted()); + wizard_p()->on_custom_setup(); }); append(cb_custom); @@ -413,7 +713,7 @@ PageUpdate::PageUpdate(ConfigWizard *parent) , version_check(true) , preset_update(true) { - const AppConfig *app_config = GUI::get_app_config(); + const AppConfig *app_config = wxGetApp().app_config; auto boldfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); boldfont.SetWeight(wxFONTWEIGHT_BOLD); @@ -445,51 +745,73 @@ PageUpdate::PageUpdate(ConfigWizard *parent) box_presets->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->preset_update = event.IsChecked(); }); } +PageMode::PageMode(ConfigWizard *parent) + : ConfigWizardPage(parent, _(L("View mode")), _(L("View mode"))) +{ + append_text(_(L("PrusaSlicer's user interfaces comes in three variants:\nSimple, Advanced, and Expert.\n" + "The Simple mode shows only the most frequently used settings relevant for regular 3D printing. " + "The other two offer progressivly more sophisticated fine-tuning, " + "they are suitable for advanced and expert usiser, respectively."))); + + radio_simple = new wxRadioButton(this, wxID_ANY, _(L("Simple mode"))); + radio_advanced = new wxRadioButton(this, wxID_ANY, _(L("Advanced mode"))); + radio_expert = new wxRadioButton(this, wxID_ANY, _(L("Expert mode"))); + + append(radio_simple); + append(radio_advanced); + append(radio_expert); +} + +void PageMode::on_activate() +{ + std::string mode { "simple" }; + wxGetApp().app_config->get("", "view_mode", mode); + + if (mode == "advanced") { radio_advanced->SetValue(true); } + else if (mode == "expert") { radio_expert->SetValue(true); } + else { radio_simple->SetValue(true); } +} + +void PageMode::serialize_mode(AppConfig *app_config) const +{ + const char *mode = "simple"; + + if (radio_advanced->GetValue()) { mode = "advanced"; } + if (radio_expert->GetValue()) { mode = "expert"; } + + app_config->set("view_mode", mode); +} + PageVendors::PageVendors(ConfigWizard *parent) : ConfigWizardPage(parent, _(L("Other Vendors")), _(L("Other Vendors"))) { - append_text(wxString::Format(_(L("Pick another vendor supported by %s:")), SLIC3R_APP_NAME)); + const AppConfig &appconfig = this->wizard_p()->appconfig_new; + + append_text(wxString::Format(_(L("Pick another vendor supported by %s: (FIXME: this text)")), SLIC3R_APP_NAME)); auto boldfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); boldfont.SetWeight(wxFONTWEIGHT_BOLD); - AppConfig &appconfig_vendors = this->wizard_p()->appconfig_vendors; - wxArrayString choices_vendors; + for (const auto &pair : wizard_p()->bundles) { + const VendorProfile *vendor = pair.second.vendor_profile; + if (vendor->id == PresetBundle::PRUSA_BUNDLE) { continue; } - for (const auto vendor_pair : wizard_p()->vendors) { - const auto &vendor = vendor_pair.second; - if (vendor.id == "PrusaResearch") { continue; } - - auto *picker = new PrinterPicker(this, vendor, "", MAX_COLS, appconfig_vendors); - picker->Hide(); - pickers.push_back(picker); - choices_vendors.Add(vendor.name); - - picker->Bind(EVT_PRINTER_PICK, [this, &appconfig_vendors](const PrinterPickerEvent &evt) { - appconfig_vendors.set_variant(evt.vendor_id, evt.model_id, evt.variant_name, evt.enable); + auto *cbox = new wxCheckBox(this, wxID_ANY, vendor->name); + cbox->Bind(wxEVT_CHECKBOX, [=](wxCommandEvent &event) { + wizard_p()->on_3rdparty_install(vendor, cbox->IsChecked()); }); - } - auto *vendor_picker = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, choices_vendors); - if (choices_vendors.GetCount() > 0) { - vendor_picker->SetSelection(0); - on_vendor_pick(0); - } + const auto &vendors = appconfig.vendors(); + const bool enabled = vendors.find(pair.first) != vendors.end(); + if (enabled) { + cbox->SetValue(true); - vendor_picker->Bind(wxEVT_CHOICE, [this](wxCommandEvent &evt) { - this->on_vendor_pick(evt.GetInt()); - }); + auto pair = wizard_p()->pages_3rdparty.find(vendor->id); + wxCHECK_RET(pair != wizard_p()->pages_3rdparty.end(), "Internal error: 3rd party vendor printers page not created"); + pair->second->install = true; + } - append(vendor_picker); - for (PrinterPicker *picker : pickers) { this->append(picker); } -} - -void PageVendors::on_vendor_pick(size_t i) -{ - for (PrinterPicker *picker : pickers) { picker->Hide(); } - if (i < pickers.size()) { - pickers[i]->Show(); - parent->Layout(); + append(cbox); } } @@ -605,18 +927,18 @@ void PageDiameters::apply_custom_config(DynamicPrintConfig &config) auto set_extrusion_width = [&config, opt_nozzle](const char *key, double dmr) { char buf[64]; sprintf(buf, "%.2lf", dmr * opt_nozzle->values.front() / 0.4); - config.set_key_value(key, new ConfigOptionFloatOrPercent(atof(buf), false)); - }; + config.set_key_value(key, new ConfigOptionFloatOrPercent(atof(buf), false)); + }; set_extrusion_width("support_material_extrusion_width", 0.35); - set_extrusion_width("top_infill_extrusion_width", 0.40); - set_extrusion_width("first_layer_extrusion_width", 0.42); + set_extrusion_width("top_infill_extrusion_width", 0.40); + set_extrusion_width("first_layer_extrusion_width", 0.42); - set_extrusion_width("extrusion_width", 0.45); - set_extrusion_width("perimeter_extrusion_width", 0.45); - set_extrusion_width("external_perimeter_extrusion_width", 0.45); - set_extrusion_width("infill_extrusion_width", 0.45); - set_extrusion_width("solid_infill_extrusion_width", 0.45); + set_extrusion_width("extrusion_width", 0.45); + set_extrusion_width("perimeter_extrusion_width", 0.45); + set_extrusion_width("external_perimeter_extrusion_width", 0.45); + set_extrusion_width("infill_extrusion_width", 0.45); + set_extrusion_width("solid_infill_extrusion_width", 0.45); } PageTemperatures::PageTemperatures(ConfigWizard *parent) @@ -684,8 +1006,8 @@ ConfigWizardIndex::ConfigWizardIndex(wxWindow *parent) , bullet_black(ScalableBitmap(parent, "bullet_black.png")) , bullet_blue(ScalableBitmap(parent, "bullet_blue.png")) , bullet_white(ScalableBitmap(parent, "bullet_white.png")) - , item_active(0) - , item_hover(-1) + , item_active(NO_ITEM) + , item_hover(NO_ITEM) , last_page((size_t)-1) { SetMinSize(bg.bmp().GetSize()); @@ -747,6 +1069,8 @@ void ConfigWizardIndex::go_prev() { // Search for a preceiding item that is a page (not a label, ie. page != nullptr) + if (item_active == NO_ITEM) { return; } + for (size_t i = item_active; i > 0; i--) { if (items[i - 1].page != nullptr) { go_to(i - 1); @@ -759,6 +1083,8 @@ void ConfigWizardIndex::go_next() { // Search for a next item that is a page (not a label, ie. page != nullptr) + if (item_active == NO_ITEM) { return; } + for (size_t i = item_active + 1; i < items.size(); i++) { if (items[i].page != nullptr) { go_to(i); @@ -767,28 +1093,39 @@ void ConfigWizardIndex::go_next() } } +// This one actually performs the go-to op void ConfigWizardIndex::go_to(size_t i) { - if (i < items.size() && items[i].page != nullptr) { + if (i != item_active + && i < items.size() + && items[i].page != nullptr) { + auto *new_active = items[i].page; auto *former_active = active_page(); - if (former_active != nullptr) { former_active->Hide(); } + if (former_active != nullptr) { + former_active->Hide(); + } item_active = i; - items[i].page->Show(); + new_active->Show(); wxCommandEvent evt(EVT_INDEX_PAGE, GetId()); AddPendingEvent(evt); Refresh(); + + new_active->on_activate(); } } -void ConfigWizardIndex::go_to(ConfigWizardPage *page) +void ConfigWizardIndex::go_to(const ConfigWizardPage *page) { if (page == nullptr) { return; } for (size_t i = 0; i < items.size(); i++) { - if (items[i].page == page) { go_to(i); } + if (items[i].page == page) { + go_to(i); + return; + } } } @@ -798,7 +1135,7 @@ void ConfigWizardIndex::clear() if (former_active != nullptr) { former_active->Hide(); } items.clear(); - item_active = 0; + item_active = NO_ITEM; } void ConfigWizardIndex::on_paint(wxPaintEvent & evt) @@ -850,7 +1187,7 @@ void ConfigWizardIndex::on_mouse_move(wxMouseEvent &evt) const ssize_t item_hover_new = pos.y / item_height(); - if (item_hover_new < ssize_t(items.size()) && item_hover_new != item_hover) { + if (item_hover_new < ssize_t(items.size()) && item_hover_new != item_hover) { item_hover = item_hover_new; Refresh(); } @@ -875,6 +1212,72 @@ void ConfigWizardIndex::msw_rescale() } +// Materials + +const std::string Materials::UNKNOWN = "(Unknown)"; + +void Materials::push(const Preset *preset) +{ + presets.insert(preset); + types.insert(technology & T_FFF + ? Materials::get_filament_type(preset) + : Materials::get_material_type(preset)); +} + +void Materials::clear() +{ + presets.clear(); + types.clear(); +} + +const std::string& Materials::appconfig_section() const +{ + return (technology & T_FFF) ? AppConfig::SECTION_FILAMENTS : AppConfig::SECTION_MATERIALS; +} + +const std::string& Materials::get_type(const Preset *preset) const +{ + return (technology & T_FFF) ? get_filament_type(preset) : get_material_type(preset); +} + +const std::string& Materials::get_vendor(const Preset *preset) const +{ + return (technology & T_FFF) ? get_filament_vendor(preset) : get_material_vendor(preset); +} + +const std::string& Materials::get_filament_type(const Preset *preset) +{ + const auto *opt = preset->config.opt("filament_type"); + if (opt != nullptr && opt->values.size() > 0) { + return opt->values[0]; + } else { + return UNKNOWN; + } +} + +const std::string& Materials::get_filament_vendor(const Preset *preset) +{ + const auto *opt = preset->config.opt("filament_vendor"); + return opt != nullptr ? opt->value : UNKNOWN; +} + +const std::string& Materials::get_material_type(const Preset *preset) +{ + const auto *opt = preset->config.opt("material_type"); + if (opt != nullptr) { + return opt->value; + } else { + return UNKNOWN; + } +} + +const std::string& Materials::get_material_vendor(const Preset *preset) +{ + const auto *opt = preset->config.opt("material_vendor"); + return opt != nullptr ? opt->value : UNKNOWN; +} + + // priv static const std::unordered_map> legacy_preset_map {{ @@ -888,25 +1291,40 @@ static const std::unordered_map { "Original Prusa i3 MK3.ini", std::make_pair("MK3", "0.4") }, }}; -void ConfigWizard::priv::load_pages(bool custom_setup) +void ConfigWizard::priv::load_pages() { - const auto former_active = index->active_item(); + wxWindowUpdateLocker freeze_guard(q); + (void)freeze_guard; + + const ConfigWizardPage *former_active = index->active_page(); index->clear(); index->add_page(page_welcome); + + // Printers index->add_page(page_fff); index->add_page(page_msla); - index->add_page(page_custom); + index->add_page(page_vendors); + for (const auto &pair : pages_3rdparty) { + PagePrinters *page = pair.second; + if (page->install) { index->add_page(page); } + } - if (custom_setup) { + index->add_page(page_custom); + if (page_custom->custom_wanted()) { index->add_page(page_firmware); index->add_page(page_bed); index->add_page(page_diams); index->add_page(page_temps); } + // Filaments & Materials + if (any_fff_selected) { index->add_page(page_filaments); } + if (any_sla_selected) { index->add_page(page_sla_materials); } + index->add_page(page_update); + index->add_page(page_mode); index->go_to(former_active); // Will restore the active item/page if possible @@ -936,48 +1354,14 @@ void ConfigWizard::priv::init_dialog_size() q->SetSize(window_rect); } -bool ConfigWizard::priv::check_first_variant() const -{ - return run_reason == RR_DATA_EMPTY || run_reason == RR_DATA_LEGACY; -} - void ConfigWizard::priv::load_vendors() { - const auto vendor_dir = fs::path(Slic3r::data_dir()) / "vendor"; - const auto rsrc_vendor_dir = fs::path(resources_dir()) / "profiles"; - - // Load vendors from the "vendors" directory in datadir - for (auto &dir_entry : boost::filesystem::directory_iterator(vendor_dir)) - if (Slic3r::is_ini_file(dir_entry)) { - try { - auto vp = VendorProfile::from_ini(dir_entry.path()); - vendors[vp.id] = std::move(vp); - } - catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(error) << boost::format("Error loading vendor bundle %1%: %2%") % dir_entry.path() % e.what(); - } - } - - // Additionally load up vendors from the application resources directory, but only those not seen in the datadir - for (auto &dir_entry : boost::filesystem::directory_iterator(rsrc_vendor_dir)) - if (Slic3r::is_ini_file(dir_entry)) { - const auto id = dir_entry.path().stem().string(); - if (vendors.find(id) == vendors.end()) { - try { - auto vp = VendorProfile::from_ini(dir_entry.path()); - vendors_rsrc[vp.id] = dir_entry.path().filename().string(); - vendors[vp.id] = std::move(vp); - } - catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(error) << boost::format("Error loading vendor bundle %1%: %2%") % dir_entry.path() % e.what(); - } - } - } + bundles = BundleMap::load(); // Load up the set of vendors / models / variants the user has had enabled up till now - const AppConfig *app_config = GUI::get_app_config(); + AppConfig *app_config = wxGetApp().app_config; if (! app_config->legacy_datadir()) { - appconfig_vendors.set_vendors(*app_config); + appconfig_new.set_vendors(*app_config); } else { // In case of legacy datadir, try to guess the preference based on the printer preset files that are present const auto printer_dir = fs::path(Slic3r::data_dir()) / "printer"; @@ -988,14 +1372,29 @@ void ConfigWizard::priv::load_vendors() const auto &model = needle->second.first; const auto &variant = needle->second.second; - appconfig_vendors.set_variant("PrusaResearch", model, variant, true); + appconfig_new.set_variant("PrusaResearch", model, variant, true); } } + + // Initialize the is_visible flag in printer Presets + for (auto &pair : bundles) { + pair.second.preset_bundle->load_installed_printers(appconfig_new); + } + + update_materials(T_ANY); + + if (app_config->has_section(AppConfig::SECTION_FILAMENTS)) { + appconfig_new.set_section(AppConfig::SECTION_FILAMENTS, app_config->get_section(AppConfig::SECTION_FILAMENTS)); + } + if (app_config->has_section(AppConfig::SECTION_MATERIALS)) { + appconfig_new.set_section(AppConfig::SECTION_MATERIALS, app_config->get_section(AppConfig::SECTION_MATERIALS)); + } } void ConfigWizard::priv::add_page(ConfigWizardPage *page) { hscroll_sizer->Add(page, 0, wxEXPAND); + all_pages.push_back(page); } void ConfigWizard::priv::enable_next(bool enable) @@ -1004,19 +1403,158 @@ void ConfigWizard::priv::enable_next(bool enable) btn_finish->Enable(enable); } -void ConfigWizard::priv::on_custom_setup(bool custom_wanted) +void ConfigWizard::priv::set_start_page(ConfigWizard::StartPage start_page) { - load_pages(custom_wanted); + switch (start_page) { + case ConfigWizard::SP_PRINTERS: index->go_to(page_fff); break; + case ConfigWizard::SP_FILAMENTS: index->go_to(page_filaments); break; + case ConfigWizard::SP_MATERIALS: index->go_to(page_sla_materials); break; + default: index->go_to(page_welcome); break; + } +} + +void ConfigWizard::priv::create_3rdparty_pages() +{ + for (const auto &pair : bundles) { + const VendorProfile *vendor = pair.second.vendor_profile; + if (vendor->id == PresetBundle::PRUSA_BUNDLE) { continue; } + + auto *page = new PagePrinters(q, vendor->name, vendor->name, *vendor, 1, T_ANY); + add_page(page); + + pages_3rdparty.insert({vendor->id, page}); + } +} + +void ConfigWizard::priv::set_run_reason(RunReason run_reason) +{ + this->run_reason = run_reason; + for (auto &page : all_pages) { + page->set_run_reason(run_reason); + } +} + +void ConfigWizard::priv::update_materials(Technology technology) +{ + if (any_fff_selected && (technology & T_FFF)) { + filaments.clear(); + + // Iterate filaments in all bundles + for (const auto &pair : bundles) { + for (const auto &filament : pair.second.preset_bundle->filaments) { + // Check if filament is already added + if (filaments.containts(&filament)) { continue; } + + // Iterate printers in all bundles + for (const auto &pair : bundles) { + for (const auto &printer : pair.second.preset_bundle->printers) { + // Filter out inapplicable printers + if (!printer.is_visible || printer.printer_technology() != ptFFF) { + continue; + } + + if (filament.is_compatible_with_printer(printer)) { + filaments.push(&filament); + } + } + } + } + } + } + + if (any_sla_selected && (technology & T_SLA)) { + sla_materials.clear(); + + // Iterate SLA materials in all bundles + for (const auto &pair : bundles) { + for (const auto &material : pair.second.preset_bundle->sla_materials) { + // Check if material is already added + if (sla_materials.containts(&material)) { continue; } + + // Iterate printers in all bundles + for (const auto &pair : bundles) { + for (const auto &printer : pair.second.preset_bundle->printers) { + // Filter out inapplicable printers + if (!printer.is_visible || printer.printer_technology() != ptSLA) { + continue; + } + + if (material.is_compatible_with_printer(printer)) { + sla_materials.push(&material); + } + } + } + } + } + } +} + +void ConfigWizard::priv::on_custom_setup() +{ + load_pages(); +} + +void ConfigWizard::priv::on_printer_pick(PagePrinters *page, const PrinterPickerEvent &evt) +{ + if (page_msla->any_selected() != any_sla_selected || + page_fff->any_selected() != any_fff_selected) { + any_fff_selected = page_fff->any_selected(); + any_sla_selected = page_msla->any_selected(); + + load_pages(); + } + + // Update the is_visible flag on relevant printer profiles + for (auto &pair : bundles) { + if (pair.first != evt.vendor_id) { continue; } + + for (auto &preset : pair.second.preset_bundle->printers) { + if (preset.config.opt_string("printer_model") == evt.model_id + && preset.config.opt_string("printer_variant") == evt.variant_name) { + preset.is_visible = evt.enable; + } + } + } + + if (page == page_fff) { + page_filaments->clear(); + } else if (page == page_msla) { + page_sla_materials->clear(); + } +} + +void ConfigWizard::priv::on_3rdparty_install(const VendorProfile *vendor, bool install) +{ + auto it = pages_3rdparty.find(vendor->id); + wxCHECK_RET(it != pages_3rdparty.end(), "Internal error: GUI page not found for 3rd party vendor profile"); + PagePrinters *page = it->second; + + if (page->install && !install) { + page->select_all(false); + } + page->install = install; + page->Layout(); + + load_pages(); } void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater) { - const auto enabled_vendors = appconfig_vendors.vendors(); + const auto enabled_vendors = appconfig_new.vendors(); // Install bundles from resources if needed: std::vector install_bundles; - for (const auto &vendor_rsrc : vendors_rsrc) { - const auto vendor = enabled_vendors.find(vendor_rsrc.first); + for (const auto &pair : bundles) { + if (! pair.second.is_in_resources) { continue; } + + if (pair.second.is_prusa_bundle) { + // Always install Prusa bundle, because it has a lot of filaments/materials + // likely to be referenced by other profiles. + install_bundles.emplace_back(pair.first); + continue; + } + + const auto vendor = enabled_vendors.find(pair.first); if (vendor == enabled_vendors.end()) { continue; } size_t size_sum = 0; @@ -1024,7 +1562,7 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese if (size_sum > 0) { // This vendor needs to be installed - install_bundles.emplace_back(vendor_rsrc.second); + install_bundles.emplace_back(pair.first); } } @@ -1066,20 +1604,27 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese preset_bundle->reset(true); } - app_config->set_vendors(appconfig_vendors); + app_config->set_vendors(appconfig_new); + if (appconfig_new.has_section(AppConfig::SECTION_FILAMENTS)) { + app_config->set_section(AppConfig::SECTION_FILAMENTS, appconfig_new.get_section(AppConfig::SECTION_FILAMENTS)); + } + if (appconfig_new.has_section(AppConfig::SECTION_MATERIALS)) { + app_config->set_section(AppConfig::SECTION_MATERIALS, appconfig_new.get_section(AppConfig::SECTION_MATERIALS)); + } app_config->set("version_check", page_update->version_check ? "1" : "0"); app_config->set("preset_update", page_update->preset_update ? "1" : "0"); + page_mode->serialize_mode(app_config); std::string preferred_model; - // Figure out the default pre-selected printer based on the seletions in the picker. + // Figure out the default pre-selected printer based on the selections in the pickers. // The default is the first selected printer model (one with at least 1 variant selected). // The default is only applied by load_presets() if the user doesn't have a (visible) printer // selected already. - const auto vendor_prusa = vendors.find("PrusaResearch"); + // Prusa printers are considered first, then 3rd party. const auto config_prusa = enabled_vendors.find("PrusaResearch"); - if (vendor_prusa != vendors.end() && config_prusa != enabled_vendors.end()) { - for (const auto &model : vendor_prusa->second.models) { + if (config_prusa != enabled_vendors.end()) { + for (const auto &model : bundles.prusa_bundle().vendor_profile->models) { const auto model_it = config_prusa->second.find(model.id); if (model_it != config_prusa->second.end() && model_it->second.size() > 0) { preferred_model = model.id; @@ -1087,6 +1632,20 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese } } } + if (preferred_model.empty()) { + for (const auto &bundle : bundles) { + if (bundle.second.is_prusa_bundle) { continue; } + + const auto config = enabled_vendors.find(bundle.first); + for (const auto &model : bundle.second.vendor_profile->models) { + const auto model_it = config->second.find(model.id); + if (model_it != config->second.end() && model_it->second.size() > 0) { + preferred_model = model.id; + break; + } + } + } + } preset_bundle->load_presets(*app_config, preferred_model); @@ -1104,14 +1663,14 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese preset_bundle->export_selections(*app_config); } + // Public -ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) +ConfigWizard::ConfigWizard(wxWindow *parent) : DPIDialog(parent, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + name(), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) , p(new priv(this)) { this->SetFont(wxGetApp().normal_font()); - p->run_reason = reason; p->load_vendors(); p->custom_config.reset(DynamicPrintConfig::new_from_defaults_keys({ @@ -1148,28 +1707,40 @@ ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) p->btnsizer->Add(p->btn_finish, 0, wxLEFT, BTN_SPACING); p->btnsizer->Add(p->btn_cancel, 0, wxLEFT, BTN_SPACING); - const auto &vendors = p->vendors; - const auto vendor_prusa_it = vendors.find("PrusaResearch"); - wxCHECK_RET(vendor_prusa_it != vendors.cend(), "Vendor PrusaResearch not found"); - const VendorProfile &vendor_prusa = vendor_prusa_it->second; + const auto prusa_it = p->bundles.find("PrusaResearch"); + wxCHECK_RET(prusa_it != p->bundles.cend(), "Vendor PrusaResearch not found"); + const VendorProfile *vendor_prusa = prusa_it->second.vendor_profile; p->add_page(p->page_welcome = new PageWelcome(this)); - p->page_fff = new PagePrinters(this, _(L("Prusa FFF Technology Printers")), "Prusa FFF", vendor_prusa, 0, PagePrinters::T_FFF); + p->page_fff = new PagePrinters(this, _(L("Prusa FFF Technology Printers")), "Prusa FFF", *vendor_prusa, 0, T_FFF); p->add_page(p->page_fff); - p->page_msla = new PagePrinters(this, _(L("Prusa MSLA Technology Printers")), "Prusa MSLA", vendor_prusa, 0, PagePrinters::T_SLA); + p->page_msla = new PagePrinters(this, _(L("Prusa MSLA Technology Printers")), "Prusa MSLA", *vendor_prusa, 0, T_SLA); p->add_page(p->page_msla); + p->add_page(p->page_filaments = new PageMaterials(this, &p->filaments, + _(L("Filament Profiles Selection")), _(L("Filaments")), _(L("Type:")) )); + p->add_page(p->page_sla_materials = new PageMaterials(this, &p->sla_materials, + _(L("SLA Material Profiles Selection")), _(L("SLA Materials")), _(L("Layer height:")) )); + p->add_page(p->page_custom = new PageCustom(this)); p->add_page(p->page_update = new PageUpdate(this)); - p->add_page(p->page_vendors = new PageVendors(this)); + p->add_page(p->page_mode = new PageMode(this)); p->add_page(p->page_firmware = new PageFirmware(this)); p->add_page(p->page_bed = new PageBedShape(this)); p->add_page(p->page_diams = new PageDiameters(this)); p->add_page(p->page_temps = new PageTemperatures(this)); - p->load_pages(false); + // Pages for 3rd party vendors + p->create_3rdparty_pages(); // Needs to ne done _before_ creating PageVendors + p->add_page(p->page_vendors = new PageVendors(this)); + + p->any_sla_selected = p->page_msla->any_selected(); + p->any_fff_selected = p->page_fff->any_selected(); + + p->load_pages(); + p->index->go_to(size_t{0}); vsizer->Add(topsizer, 1, wxEXPAND | wxALL, DIALOG_MARGIN); vsizer->Add(hline, 0, wxEXPAND); @@ -1191,9 +1762,11 @@ ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) p->btn_finish->Hide(); p->btn_sel_all->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &) { + p->any_sla_selected = true; + p->load_pages(); p->page_fff->select_all(true, false); p->page_msla->select_all(true, false); - p->index->go_to(p->page_update); + p->index->go_to(p->page_mode); }); p->index->Bind(EVT_INDEX_PAGE, [this](const wxCommandEvent &) { @@ -1207,13 +1780,19 @@ ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) ConfigWizard::~ConfigWizard() {} -bool ConfigWizard::run(PresetBundle *preset_bundle, const PresetUpdater *updater) +bool ConfigWizard::run(RunReason reason, StartPage start_page) { - BOOST_LOG_TRIVIAL(info) << "Running ConfigWizard, reason: " << p->run_reason; + BOOST_LOG_TRIVIAL(info) << boost::format("Running ConfigWizard, reason: %1%, start_page: %2%") % reason % start_page; + + GUI_App &app = wxGetApp(); + + p->set_run_reason(reason); + p->set_start_page(start_page); + if (ShowModal() == wxID_OK) { - auto *app_config = GUI::get_app_config(); - p->apply_config(app_config, preset_bundle, updater); - app_config->set_legacy_datadir(false); + p->apply_config(app.app_config, app.preset_bundle, app.preset_updater); + app.app_config->set_legacy_datadir(false); + app.update_mode(); BOOST_LOG_TRIVIAL(info) << "ConfigWizard applied"; return true; } else { @@ -1222,7 +1801,6 @@ bool ConfigWizard::run(PresetBundle *preset_bundle, const PresetUpdater *updater } } - const wxString& ConfigWizard::name(const bool from_menu/* = false*/) { // A different naming convention is used for the Wizard on Windows & GTK vs. OSX. @@ -1243,7 +1821,7 @@ void ConfigWizard::on_dpi_changed(const wxRect &suggested_rect) { p->index->msw_rescale(); - const int& em = em_unit(); + const int em = em_unit(); msw_buttons_rescale(this, em, { wxID_APPLY, wxID_CANCEL, diff --git a/src/slic3r/GUI/ConfigWizard.hpp b/src/slic3r/GUI/ConfigWizard.hpp index b707e525ba..942f4b4ce8 100644 --- a/src/slic3r/GUI/ConfigWizard.hpp +++ b/src/slic3r/GUI/ConfigWizard.hpp @@ -26,7 +26,15 @@ public: RR_USER, // User requested the Wizard from the menus }; - ConfigWizard(wxWindow *parent, RunReason run_reason); + // What page should wizard start on + enum StartPage { + SP_WELCOME, + SP_PRINTERS, + SP_FILAMENTS, + SP_MATERIALS, + }; + + ConfigWizard(wxWindow *parent); ConfigWizard(ConfigWizard &&) = delete; ConfigWizard(const ConfigWizard &) = delete; ConfigWizard &operator=(ConfigWizard &&) = delete; @@ -34,7 +42,7 @@ public: ~ConfigWizard(); // Run the Wizard. Return whether it was completed. - bool run(PresetBundle *preset_bundle, const PresetUpdater *updater); + bool run(RunReason reason, StartPage start_page = SP_WELCOME); static const wxString& name(const bool from_menu = false); diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp index f4848933fc..995957816f 100644 --- a/src/slic3r/GUI/ConfigWizard_private.hpp +++ b/src/slic3r/GUI/ConfigWizard_private.hpp @@ -15,11 +15,14 @@ #include #include #include +#include +#include +#include #include "libslic3r/PrintConfig.hpp" #include "slic3r/Utils/PresetUpdater.hpp" #include "AppConfig.hpp" -#include "Preset.hpp" +#include "PresetBundle.hpp" #include "BedShapeDialog.hpp" namespace fs = boost::filesystem; @@ -41,6 +44,76 @@ enum { ROW_SPACING = 75, }; + + +// Configuration data structures extensions needed for the wizard + +enum Technology { + // Bitflag equivalent of PrinterTechnology + T_FFF = 0x1, + T_SLA = 0x2, + T_ANY = ~0, +}; + +struct Materials +{ + Technology technology; + std::set presets; + std::set types; + + Materials(Technology technology) : technology(technology) {} + + void push(const Preset *preset); + void clear(); + bool containts(const Preset *preset) { + return presets.find(preset) != presets.end(); + } + + const std::string& appconfig_section() const; + const std::string& get_type(const Preset *preset) const; + const std::string& get_vendor(const Preset *preset) const; + + template void filter_presets(const std::string &type, const std::string &vendor, F cb) { + for (const Preset *preset : presets) { + if ((type.empty() || get_type(preset) == type) && (vendor.empty() || get_vendor(preset) == vendor)) { + cb(preset); + } + } + } + + static const std::string UNKNOWN; + static const std::string& get_filament_type(const Preset *preset); + static const std::string& get_filament_vendor(const Preset *preset); + static const std::string& get_material_type(const Preset *preset); + static const std::string& get_material_vendor(const Preset *preset); +}; + +struct Bundle +{ + std::unique_ptr preset_bundle; + VendorProfile *vendor_profile; + const bool is_in_resources; + const bool is_prusa_bundle; + + Bundle(fs::path source_path, bool is_in_resources, bool is_prusa_bundle = false); + Bundle(Bundle &&other); + + const std::string& vendor_id() const { return vendor_profile->id; } +}; + +struct BundleMap: std::unordered_map +{ + static BundleMap load(); + + Bundle& prusa_bundle(); + const Bundle& prusa_bundle() const; +}; + +struct PrinterPickerEvent; + + +// GUI elements + typedef std::function ModelFilter; struct PrinterPicker: wxPanel @@ -61,19 +134,22 @@ struct PrinterPicker: wxPanel std::vector cboxes; std::vector cboxes_alt; - PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig_vendors, const ModelFilter &filter); - PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig_vendors); + PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig, const ModelFilter &filter); + PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxString title, size_t max_cols, const AppConfig &appconfig); void select_all(bool select, bool alternates = false); void select_one(size_t i, bool select); - void on_checkbox(const Checkbox *cbox, bool checked); + bool any_selected() const; int get_width() const { return width; } const std::vector& get_button_indexes() { return m_button_indexes; } + + static const std::string PRINTER_PLACEHOLDER; private: int width; - std::vector m_button_indexes; + + void on_checkbox(const Checkbox *cbox, bool checked); }; struct ConfigWizardPage: wxPanel @@ -87,43 +163,107 @@ struct ConfigWizardPage: wxPanel virtual ~ConfigWizardPage(); template - void append(T *thing, int proportion = 0, int flag = wxEXPAND|wxTOP|wxBOTTOM, int border = 10) + T* append(T *thing, int proportion = 0, int flag = wxEXPAND|wxTOP|wxBOTTOM, int border = 10) { content->Add(thing, proportion, flag, border); + return thing; } - void append_text(wxString text); + wxStaticText* append_text(wxString text); void append_spacer(int space); ConfigWizard::priv *wizard_p() const { return parent->p.get(); } virtual void apply_custom_config(DynamicPrintConfig &config) {} + virtual void set_run_reason(ConfigWizard::RunReason run_reason) {} + virtual void on_activate() {} }; struct PageWelcome: ConfigWizardPage { + wxStaticText *welcome_text; wxCheckBox *cbox_reset; PageWelcome(ConfigWizard *parent); bool reset_user_profile() const { return cbox_reset != nullptr ? cbox_reset->GetValue() : false; } + + virtual void set_run_reason(ConfigWizard::RunReason run_reason) override; }; struct PagePrinters: ConfigWizardPage { - enum Technology { - // Bitflag equivalent of PrinterTechnology - T_FFF = 0x1, - T_SLA = 0x2, - T_Any = ~0, - }; - std::vector printer_pickers; + Technology technology; + bool install; - PagePrinters(ConfigWizard *parent, wxString title, wxString shortname, const VendorProfile &vendor, unsigned indent, Technology technology); + PagePrinters(ConfigWizard *parent, + wxString title, + wxString shortname, + const VendorProfile &vendor, + unsigned indent, Technology technology); void select_all(bool select, bool alternates = false); int get_width() const; + bool any_selected() const; + + virtual void set_run_reason(ConfigWizard::RunReason run_reason) override; +}; + +// Here we extend wxListBox and wxCheckListBox +// to make the client data API much easier to use. +template struct DataList : public T +{ + DataList(wxWindow *parent) : T(parent, wxID_ANY) {} + + // Note: We're _not_ using wxLB_SORT here because it doesn't do the right thing, + // eg. "ABS" is sorted before "(All)" + + int append(const std::string &label, const D *data) { + void *ptr = reinterpret_cast(const_cast(data)); + return this->Append(from_u8(label), ptr); + } + + int append(const wxString &label, const D *data) { + void *ptr = reinterpret_cast(const_cast(data)); + return this->Append(label, ptr); + } + + const D& get_data(int n) { + return *reinterpret_cast(this->GetClientData(n)); + } + + int find(const D &data) { + for (unsigned i = 0; i < this->GetCount(); i++) { + if (get_data(i) == data) { return i; } + } + + return wxNOT_FOUND; + } +}; + +typedef DataList StringList; +typedef DataList PresetList; + +struct PageMaterials: ConfigWizardPage +{ + Materials *materials; + StringList *list_l1, *list_l2; + PresetList *list_l3; + int sel1_prev, sel2_prev; + bool presets_loaded; + + static const std::string EMPTY; + + PageMaterials(ConfigWizard *parent, Materials *materials, wxString title, wxString shortname, wxString list1name); + + void reload_presets(); + void update_lists(int sel1, int sel2); + void select_material(int i); + void select_all(bool select); + void clear(); + + virtual void on_activate() override; }; struct PageCustom: ConfigWizardPage @@ -150,13 +290,22 @@ struct PageUpdate: ConfigWizardPage PageUpdate(ConfigWizard *parent); }; +struct PageMode: ConfigWizardPage +{ + wxRadioButton *radio_simple; + wxRadioButton *radio_advanced; + wxRadioButton *radio_expert; + + PageMode(ConfigWizard *parent); + + void serialize_mode(AppConfig *app_config) const; + + virtual void on_activate(); +}; + struct PageVendors: ConfigWizardPage { - std::vector pickers; - PageVendors(ConfigWizard *parent); - - void on_vendor_pick(size_t i); }; struct PageFirmware: ConfigWizardPage @@ -194,6 +343,8 @@ struct PageTemperatures: ConfigWizardPage virtual void apply_custom_config(DynamicPrintConfig &config); }; +typedef std::map Pages3rdparty; + class ConfigWizardIndex: public wxPanel { @@ -210,12 +361,14 @@ public: void go_prev(); void go_next(); void go_to(size_t i); - void go_to(ConfigWizardPage *page); + void go_to(const ConfigWizardPage *page); void clear(); void msw_rescale(); int em() const { return em_w; } + + static const size_t NO_ITEM = size_t(-1); private: struct Item { @@ -228,12 +381,6 @@ private: int em_w; int em_h; - /* #ys_FIXME_delete_after_testing by VK - const wxBitmap bg; - const wxBitmap bullet_black; - const wxBitmap bullet_blue; - const wxBitmap bullet_white; - */ ScalableBitmap bg; ScalableBitmap bullet_black; ScalableBitmap bullet_blue; @@ -245,9 +392,6 @@ private: ssize_t item_hover; size_t last_page; - /* #ys_FIXME_delete_after_testing by VK - int item_height() const { return std::max(bullet_black.GetSize().GetHeight(), em_w) + em_w; } - */ int item_height() const { return std::max(bullet_black.bmp().GetSize().GetHeight(), em_w) + em_w; } void on_paint(wxPaintEvent &evt); @@ -256,14 +400,24 @@ private: wxDEFINE_EVENT(EVT_INDEX_PAGE, wxCommandEvent); + + +// ConfigWizard private data + struct ConfigWizard::priv { ConfigWizard *q; - ConfigWizard::RunReason run_reason; - AppConfig appconfig_vendors; - std::unordered_map vendors; - std::unordered_map vendors_rsrc; - std::unique_ptr custom_config; + ConfigWizard::RunReason run_reason = RR_USER; + AppConfig appconfig_new; // Backing for vendor/model/variant and material selections in the GUI + BundleMap bundles; // Holds all loaded config bundles, the key is the vendor names. + // Materials refers to Presets in those bundles by pointers. + // Also we update the is_visible flag in printer Presets according to the + // PrinterPickers state. + Materials filaments; // Holds available filament presets and their types & vendors + Materials sla_materials; // Ditto for SLA materials + std::unique_ptr custom_config; // Backing for custom printer definition + bool any_fff_selected; // Used to decide whether to display Filaments page + bool any_sla_selected; // Used to decide whether to display SLA Materials page wxScrolledWindow *hscroll = nullptr; wxBoxSizer *hscroll_sizer = nullptr; @@ -279,9 +433,13 @@ struct ConfigWizard::priv PageWelcome *page_welcome = nullptr; PagePrinters *page_fff = nullptr; PagePrinters *page_msla = nullptr; + PageMaterials *page_filaments = nullptr; + PageMaterials *page_sla_materials = nullptr; PageCustom *page_custom = nullptr; PageUpdate *page_update = nullptr; - PageVendors *page_vendors = nullptr; // XXX: ? + PageMode *page_mode = nullptr; + PageVendors *page_vendors = nullptr; + Pages3rdparty pages_3rdparty; // Custom setup pages PageFirmware *page_firmware = nullptr; @@ -289,17 +447,30 @@ struct ConfigWizard::priv PageDiameters *page_diams = nullptr; PageTemperatures *page_temps = nullptr; - priv(ConfigWizard *q) : q(q) {} + // Pointers to all pages (regardless or whether currently part of the ConfigWizardIndex) + std::vector all_pages; - void load_pages(bool custom_setup); + priv(ConfigWizard *q) + : q(q) + , filaments(T_FFF) + , sla_materials(T_SLA) + , any_sla_selected(false) + {} + + void load_pages(); void init_dialog_size(); - bool check_first_variant() const; void load_vendors(); void add_page(ConfigWizardPage *page); void enable_next(bool enable); + void set_start_page(ConfigWizard::StartPage start_page); + void create_3rdparty_pages(); + void set_run_reason(RunReason run_reason); + void update_materials(Technology technology); - void on_custom_setup(bool custom_wanted); + void on_custom_setup(); + void on_printer_pick(PagePrinters *page, const PrinterPickerEvent &evt); + void on_3rdparty_install(const VendorProfile *vendor, bool install); void apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 46660ab123..f80e0d0dda 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2323,7 +2323,8 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) m_dirty |= m_undoredo_toolbar.update_items_state(); m_dirty |= m_view_toolbar.update_items_state(); #if ENABLE_3DCONNEXION_DEVICES - m_dirty |= wxGetApp().plater()->get_mouse3d_controller().apply(m_camera); + bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(m_camera); + m_dirty |= mouse3d_controller_applied; #endif // ENABLE_3DCONNEXION_DEVICES if (!m_dirty) @@ -2332,7 +2333,7 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) _refresh_if_shown_on_screen(); #if ENABLE_3DCONNEXION_DEVICES - if (m_keep_dirty) + if (m_keep_dirty || mouse3d_controller_applied) { m_dirty = true; evt.RequestMore(); @@ -2585,9 +2586,9 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) { #if ENABLE_3DCONNEXION_DEVICES - // try to filter out events coming from mouse 3d controller - const Mouse3DController& controller = wxGetApp().plater()->get_mouse3d_controller(); - if (controller.has_translation_or_rotation()) + // try to filter out events coming from mouse 3d + Mouse3DController& controller = wxGetApp().plater()->get_mouse3d_controller(); + if (controller.process_mouse_wheel()) return; #endif // ENABLE_3DCONNEXION_DEVICES diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index 6e8c361c82..c22fd6f792 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -101,49 +101,6 @@ const std::string& shortkey_alt_prefix() return str; } -bool config_wizard_startup(bool app_config_exists) -{ - if (!app_config_exists || wxGetApp().preset_bundle->printers.size() <= 1) { - config_wizard(ConfigWizard::RR_DATA_EMPTY); - return true; - } else if (get_app_config()->legacy_datadir()) { - // Looks like user has legacy pre-vendorbundle data directory, - // explain what this is and run the wizard - - MsgDataLegacy dlg; - dlg.ShowModal(); - - config_wizard(ConfigWizard::RR_DATA_LEGACY); - return true; - } - return false; -} - -void config_wizard(int reason) -{ - // Exit wizard if there are unsaved changes and the user cancels the action. - if (! wxGetApp().check_unsaved_changes()) - return; - - try { - ConfigWizard wizard(nullptr, static_cast(reason)); - wizard.run(wxGetApp().preset_bundle, wxGetApp().preset_updater); - } - catch (const std::exception &e) { - show_error(nullptr, e.what()); - } - - wxGetApp().load_current_presets(); - - if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA && model_has_multi_part_objects(wxGetApp().model())) - { - show_info(nullptr, - _(L("It's impossible to print multi-part object(s) with SLA technology.")) + "\n\n" + - _(L("Please check and fix your object list.")), - _(L("Attention!"))); - } -} - // opt_index = 0, by the reason of zero-index in ConfigOptionVector by default (in case only one element) void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index /*= 0*/) { diff --git a/src/slic3r/GUI/GUI.hpp b/src/slic3r/GUI/GUI.hpp index 4074c2afc8..0b904bad86 100644 --- a/src/slic3r/GUI/GUI.hpp +++ b/src/slic3r/GUI/GUI.hpp @@ -35,14 +35,6 @@ extern AppConfig* get_app_config(); extern void add_menus(wxMenuBar *menu, int event_preferences_changed, int event_language_change); -// Checks if configuration wizard needs to run, calls config_wizard if so. -// Returns whether the Wizard ran. -extern bool config_wizard_startup(bool app_config_exists); - -// Opens the configuration wizard, returns true if wizard is finished & accepted. -// The run_reason argument is actually ConfigWizard::RunReason, but int is used here because of Perl. -extern void config_wizard(int run_reason); - // Change option value in config void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index = 0); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 86523cb88a..b5e70c0a19 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -38,7 +38,6 @@ #include "../Utils/PresetUpdater.hpp" #include "../Utils/PrintHost.hpp" #include "../Utils/MacDarkMode.hpp" -#include "ConfigWizard.hpp" #include "slic3r/Config/Snapshot.hpp" #include "ConfigSnapshotDialog.hpp" #include "FirmwareDialog.hpp" @@ -46,6 +45,7 @@ #include "Tab.hpp" #include "SysInfoDialog.hpp" #include "KBShortcutsDialog.hpp" +#include "UpdateDialogs.hpp" #ifdef __WXMSW__ #include @@ -148,6 +148,7 @@ GUI_App::GUI_App() : wxApp() , m_em_unit(10) , m_imgui(new ImGuiWrapper()) + , m_wizard(nullptr) {} GUI_App::~GUI_App() @@ -204,7 +205,6 @@ bool GUI_App::on_init_inner() // supplied as argument to --datadir; in that case we should still run the wizard preset_bundle->setup_directories(); - app_conf_exists = app_config->exists(); // load settings app_conf_exists = app_config->exists(); if (app_conf_exists) { @@ -287,7 +287,7 @@ bool GUI_App::on_init_inner() } CallAfter([this] { - config_wizard_startup(app_conf_exists); + config_wizard_startup(); preset_updater->slic3r_update_notify(); preset_updater->sync(preset_bundle); }); @@ -826,7 +826,7 @@ void GUI_App::add_config_menu(wxMenuBar *menu) local_menu->Bind(wxEVT_MENU, [this, config_id_base](wxEvent &event) { switch (event.GetId() - config_id_base) { case ConfigMenuWizard: - config_wizard(ConfigWizard::RR_USER); + run_wizard(ConfigWizard::RR_USER); break; case ConfigMenuTakeSnapshot: // Take a configuration snapshot. @@ -1057,6 +1057,31 @@ void GUI_App::open_web_page_localized(const std::string &http_address) wxLaunchDefaultBrowser(http_address + "&lng=" + this->current_language_code_safe()); } +bool GUI_App::run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage start_page) +{ + wxCHECK_MSG(mainframe != nullptr, false, "Internal error: Main frame not created / null"); + + if (! m_wizard) { + m_wizard = new ConfigWizard(mainframe); + } + + const bool res = m_wizard->run(reason, start_page); + + if (res) { + load_current_presets(); + + if (preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA + && Slic3r::model_has_multi_part_objects(wxGetApp().model())) { + GUI::show_info(nullptr, + _(L("It's impossible to print multi-part object(s) with SLA technology.")) + "\n\n" + + _(L("Please check and fix your object list.")), + _(L("Attention!"))); + } + } + + return res; +} + void GUI_App::window_pos_save(wxTopLevelWindow* window, const std::string &name) { if (name.empty()) { return; } @@ -1105,6 +1130,24 @@ void GUI_App::window_pos_sanitize(wxTopLevelWindow* window) } } +bool GUI_App::config_wizard_startup() +{ + if (!app_conf_exists || preset_bundle->printers.size() <= 1) { + run_wizard(ConfigWizard::RR_DATA_EMPTY); + return true; + } else if (get_app_config()->legacy_datadir()) { + // Looks like user has legacy pre-vendorbundle data directory, + // explain what this is and run the wizard + + MsgDataLegacy dlg; + dlg.ShowModal(); + + run_wizard(ConfigWizard::RR_DATA_LEGACY); + return true; + } + return false; +} + // static method accepting a wxWindow object as first parameter // void warning_catcher{ // my($self, $message_dialog) = @_; diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index a8043e9915..c5ddc01528 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -6,6 +6,7 @@ #include "libslic3r/PrintConfig.hpp" #include "MainFrame.hpp" #include "ImGuiWrapper.hpp" +#include "ConfigWizard.hpp" #include #include @@ -69,6 +70,7 @@ enum ConfigMenuIDs { }; class Tab; +class ConfigWizard; static wxString dots("…", wxConvUTF8); @@ -96,6 +98,7 @@ class GUI_App : public wxApp std::unique_ptr m_imgui; std::unique_ptr m_printhost_job_queue; + ConfigWizard* m_wizard; // Managed by wxWindow tree public: bool OnInit() override; @@ -184,6 +187,7 @@ public: PrintHostJobQueue& printhost_job_queue() { return *m_printhost_job_queue.get(); } void open_web_page_localized(const std::string &http_address); + bool run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage start_page = ConfigWizard::SP_WELCOME); private: bool on_init_inner(); @@ -191,6 +195,9 @@ private: void window_pos_restore(wxTopLevelWindow* window, const std::string &name, bool default_maximized = false); void window_pos_sanitize(wxTopLevelWindow* window); bool select_language(); + + bool config_wizard_startup(); + #ifdef __WXMSW__ void associate_3mf_files(); #endif // __WXMSW__ diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index f32f54b2dd..2bd6d90205 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -12,7 +12,6 @@ #include #include - #include "I18N.hpp" // WARN: If updating these lists, please also update resources/udev/90-3dconnexion.rules @@ -23,6 +22,7 @@ static const std::vector _3DCONNEXION_VENDORS = 0x256F // 3DCONNECTION = 9583 // 3Dconnexion }; +// See: https://github.com/FreeSpacenav/spacenavd/blob/a9eccf34e7cac969ee399f625aef827f4f4aaec6/src/dev.c#L202 for a wider list of devices static const std::vector _3DCONNEXION_DEVICES = { 0xC623, // TRAVELER = 50723 @@ -43,13 +43,28 @@ const double Mouse3DController::State::DefaultTranslationScale = 2.5; const float Mouse3DController::State::DefaultRotationScale = 1.0; Mouse3DController::State::State() - : m_translation(Vec3d::Zero()) - , m_rotation(Vec3f::Zero()) - , m_translation_scale(DefaultTranslationScale) + : m_translation_scale(DefaultTranslationScale) , m_rotation_scale(DefaultRotationScale) + , m_mouse_wheel_counter(0) { } +bool Mouse3DController::State::process_mouse_wheel() +{ + if (m_mouse_wheel_counter > 0) + { + --m_mouse_wheel_counter; + return true; + } + else if (m_rotation.empty()) + { + m_mouse_wheel_counter = 0; + return true; + } + + return false; +} + bool Mouse3DController::State::apply(Camera& camera) { if (!wxGetApp().IsActive()) @@ -59,35 +74,35 @@ bool Mouse3DController::State::apply(Camera& camera) if (has_translation()) { - camera.set_target(camera.get_target() + m_translation_scale * (m_translation(0) * camera.get_dir_right() + m_translation(1) * camera.get_dir_forward() + m_translation(2) * camera.get_dir_up())); - m_translation = Vec3d::Zero(); + const Vec3d& translation = m_translation.front(); + camera.set_target(camera.get_target() + m_translation_scale * (translation(0) * camera.get_dir_right() + translation(1) * camera.get_dir_forward() + translation(2) * camera.get_dir_up())); + m_translation.pop(); ret = true; } if (has_rotation()) { - float theta = m_rotation_scale * m_rotation(0); - float phi = m_rotation_scale * m_rotation(2); + const Vec3f& rotation = m_rotation.front(); + float theta = m_rotation_scale * rotation(0); + float phi = m_rotation_scale * rotation(2); float sign = camera.inverted_phi ? -1.0f : 1.0f; camera.phi += sign * phi; camera.set_theta(camera.get_theta() + theta, wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA); - m_rotation = Vec3f::Zero(); + m_rotation.pop(); + ret = true; } if (has_any_button()) { - for (unsigned int i : m_buttons) + unsigned int button = m_buttons.front(); + switch (button) { - switch (i) - { - case 0: { camera.update_zoom(1.0); break; } - case 1: { camera.update_zoom(-1.0); break; } - default: { break; } - } + case 0: { camera.update_zoom(1.0); break; } + case 1: { camera.update_zoom(-1.0); break; } + default: { break; } } - - reset_buttons(); + m_buttons.pop(); ret = true; } @@ -361,7 +376,7 @@ void Mouse3DController::collect_input() if (!translation.isApprox(Vec3d::Zero())) { updated = true; - m_state.set_translation(translation); + m_state.append_translation(translation); } break; @@ -374,7 +389,7 @@ void Mouse3DController::collect_input() if (!rotation.isApprox(Vec3f::Zero())) { updated = true; - m_state.set_rotation(rotation); + m_state.append_rotation(rotation); } break; @@ -393,7 +408,7 @@ void Mouse3DController::collect_input() if (retrieved_data[1] & (0x1 << i)) { updated = true; - m_state.set_button(i); + m_state.append_button(i); } } diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index 050ef436e9..a894e312bb 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -6,6 +6,7 @@ #include "hidapi.h" +#include #include #include #include @@ -24,26 +25,40 @@ class Mouse3DController static const float DefaultRotationScale; private: - Vec3d m_translation; - Vec3f m_rotation; - std::vector m_buttons; + std::queue m_translation; + std::queue m_rotation; + std::queue m_buttons; double m_translation_scale; float m_rotation_scale; + // When the 3Dconnexion driver is running the system gets, by default, mouse wheel events when rotations around the X axis are detected. + // We want to filter these out because we are getting the data directly from the device, bypassing the driver, and those mouse wheel events interfere + // by triggering unwanted zoom in/out of the scene + // The following variable is used to count the potential mouse wheel events triggered and is updated by: + // Mouse3DController::collect_input() through the call to the append_rotation() method + // GLCanvas3D::on_mouse_wheel() through the call to the process_mouse_wheel() method + // GLCanvas3D::on_idle() through the call to the apply() method + unsigned int m_mouse_wheel_counter; + public: State(); - void set_translation(const Vec3d& translation) { m_translation = translation; } - void set_rotation(const Vec3f& rotation) { m_rotation = rotation; } - void set_button(unsigned int id) { m_buttons.push_back(id); } - void reset_buttons() { return m_buttons.clear(); } + void append_translation(const Vec3d& translation) { m_translation.push(translation); } + void append_rotation(const Vec3f& rotation) + { + m_rotation.push(rotation); + if (rotation(0) != 0.0f) + ++m_mouse_wheel_counter; + } + void append_button(unsigned int id) { m_buttons.push(id); } - bool has_translation() const { return !m_translation.isApprox(Vec3d::Zero()); } - bool has_rotation() const { return !m_rotation.isApprox(Vec3f::Zero()); } - bool has_translation_or_rotation() const { return !m_translation.isApprox(Vec3d::Zero()) && !m_rotation.isApprox(Vec3f::Zero()); } + bool has_translation() const { return !m_translation.empty(); } + bool has_rotation() const { return !m_rotation.empty(); } bool has_any_button() const { return !m_buttons.empty(); } + bool process_mouse_wheel(); + double get_translation_scale() const { return m_translation_scale; } void set_translation_scale(double scale) { m_translation_scale = scale; } @@ -72,10 +87,7 @@ public: bool is_device_connected() const { return m_device != nullptr; } bool is_running() const { return m_running; } -// bool has_translation() const { return m_state.has_translation(); } -// bool has_rotation() const { return m_state.has_rotation(); } - bool has_translation_or_rotation() const { return m_state.has_translation_or_rotation(); } -// bool has_any_button() const { return m_state.has_any_button(); } + bool process_mouse_wheel() { std::lock_guard lock(m_mutex); return m_state.process_mouse_wheel(); } bool apply(Camera& camera); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 9ddfbfb63b..235d020ee5 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -254,11 +254,18 @@ wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(15 * auto selected_item = this->GetSelection(); auto marker = reinterpret_cast(this->GetClientData(selected_item)); - if (marker == LABEL_ITEM_MARKER || marker == LABEL_ITEM_CONFIG_WIZARD) { + if (marker >= LABEL_ITEM_MARKER && marker < LABEL_ITEM_MAX) { this->SetSelection(this->last_selected); evt.StopPropagation(); - if (marker == LABEL_ITEM_CONFIG_WIZARD) - wxTheApp->CallAfter([]() { Slic3r::GUI::config_wizard(Slic3r::GUI::ConfigWizard::RR_USER); }); + if (marker >= LABEL_ITEM_WIZARD_PRINTERS) { + ConfigWizard::StartPage sp = ConfigWizard::SP_WELCOME; + switch (marker) { + case LABEL_ITEM_WIZARD_PRINTERS: sp = ConfigWizard::SP_PRINTERS; break; + case LABEL_ITEM_WIZARD_FILAMENTS: sp = ConfigWizard::SP_FILAMENTS; break; + case LABEL_ITEM_WIZARD_MATERIALS: sp = ConfigWizard::SP_MATERIALS; break; + } + wxTheApp->CallAfter([sp]() { wxGetApp().run_wizard(ConfigWizard::RR_USER, sp); }); + } } else if ( this->last_selected != selected_item || wxGetApp().get_tab(this->preset_type)->get_presets()->current_is_dirty() ) { this->last_selected = selected_item; @@ -1577,7 +1584,8 @@ struct Plater::priv size_t count = 0; // To know how much space to reserve for (auto obj : model.objects) count += obj->instances.size(); - m_selected.clear(), m_unselected.clear(); + m_selected.clear(); + m_unselected.clear(); m_selected.reserve(count + 1 /* for optional wti */); m_unselected.reserve(count + 1 /* for optional wti */); } @@ -1591,11 +1599,12 @@ struct Plater::priv // Set up arrange polygon for a ModelInstance and Wipe tower template ArrangePolygon get_arrange_poly(T *obj) const { ArrangePolygon ap = obj->get_arrange_polygon(); - ap.priority = 0; - ap.bed_idx = ap.translation.x() / bed_stride(); - ap.setter = [obj, this](const ArrangePolygon &p) { + ap.priority = 0; + ap.bed_idx = ap.translation.x() / bed_stride(); + ap.setter = [obj, this](const ArrangePolygon &p) { if (p.is_arranged()) { - auto t = p.translation; t.x() += p.bed_idx * bed_stride(); + auto t = p.translation; + t.x() += p.bed_idx * bed_stride(); obj->apply_arrange_result(t, p.rotation); } }; @@ -1626,7 +1635,8 @@ struct Plater::priv obj_sel(model.objects.size(), nullptr); for (auto &s : plater().get_selection().get_content()) - if (s.first < int(obj_sel.size())) obj_sel[s.first] = &s.second; + if (s.first < int(obj_sel.size())) + obj_sel[size_t(s.first)] = &s.second; // Go through the objects and check if inside the selection for (size_t oidx = 0; oidx < model.objects.size(); ++oidx) { @@ -1636,7 +1646,8 @@ struct Plater::priv std::vector inst_sel(mo->instances.size(), false); if (instlist) - for (auto inst_id : *instlist) inst_sel[inst_id] = true; + for (auto inst_id : *instlist) + inst_sel[size_t(inst_id)] = true; for (size_t i = 0; i < inst_sel.size(); ++i) { ArrangePolygon &&ap = get_arrange_poly(mo->instances[i]); @@ -2773,9 +2784,8 @@ void Plater::priv::ArrangeJob::process() { try { arrangement::arrange(m_selected, m_unselected, min_d, bedshape, [this, count](unsigned st) { - if (st > - 0) // will not finalize after last one - update_status(count - st, arrangestr); + if (st > 0) // will not finalize after last one + update_status(int(count - st), arrangestr); }, [this]() { return was_canceled(); }); } catch (std::exception & /*e*/) { @@ -4909,7 +4919,11 @@ const DynamicPrintConfig* Plater::get_plater_config() const std::vector Plater::get_extruder_colors_from_plater_config() const { const Slic3r::DynamicPrintConfig* config = &wxGetApp().preset_bundle->printers.get_edited_preset().config; - std::vector extruder_colors = (config->option("extruder_colour"))->values; + std::vector extruder_colors; + if (!config->has("extruder_colour")) // in case of a SLA print + return extruder_colors; + + extruder_colors = (config->option("extruder_colour"))->values; const std::vector& filament_colours = (p->config->option("filament_colour"))->values; for (size_t i = 0; i < extruder_colors.size(); ++i) if (extruder_colors[i] == "" && i < filament_colours.size()) diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index f8ad39d38c..be3e804994 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -57,8 +57,12 @@ public: ScalableButton* edit_btn { nullptr }; enum LabelItemType { - LABEL_ITEM_MARKER = 0x4d, - LABEL_ITEM_CONFIG_WIZARD = 0x4e + LABEL_ITEM_MARKER = 0xffffff01, + LABEL_ITEM_WIZARD_PRINTERS, + LABEL_ITEM_WIZARD_FILAMENTS, + LABEL_ITEM_WIZARD_MATERIALS, + + LABEL_ITEM_MAX, }; void set_label_marker(int item, LabelItemType label_item_type = LABEL_ITEM_MARKER); diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index d84860b294..ae995aa447 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -99,6 +99,9 @@ static const std::unordered_map pre_family_model_map { VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem::path &path, bool load_all) { static const std::string printer_model_key = "printer_model:"; + static const std::string filaments_section = "default_filaments"; + static const std::string materials_section = "default_sla_materials"; + const std::string id = path.stem().string(); if (! boost::filesystem::exists(path)) { @@ -107,6 +110,7 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem VendorProfile res(id); + // Helper to get compulsory fields auto get_or_throw = [&](const ptree &tree, const std::string &key) -> ptree::const_assoc_iterator { auto res = tree.find(key); @@ -116,6 +120,7 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem return res; }; + // Load the header const auto &vendor_section = get_or_throw(tree, "vendor")->second; res.name = get_or_throw(vendor_section, "name")->second.data(); @@ -127,6 +132,7 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem res.config_version = std::move(*config_version); } + // Load URLs const auto config_update_url = vendor_section.find("config_update_url"); if (config_update_url != vendor_section.not_found()) { res.config_update_url = config_update_url->second.data(); @@ -141,6 +147,7 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem return res; } + // Load printer models for (auto §ion : tree) { if (boost::starts_with(section.first, printer_model_key)) { VendorProfile::PrinterModel model; @@ -182,6 +189,24 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem } } + // Load filaments and sla materials to be installed by default + const auto filaments = tree.find(filaments_section); + if (filaments != tree.not_found()) { + for (auto &pair : filaments->second) { + if (pair.second.data() == "1") { + res.default_filaments.insert(pair.first); + } + } + } + const auto materials = tree.find(materials_section); + if (materials != tree.not_found()) { + for (auto &pair : materials->second) { + if (pair.second.data() == "1") { + res.default_sla_materials.insert(pair.first); + } + } + } + return res; } @@ -351,10 +376,17 @@ bool Preset::update_compatible(const Preset &active_printer, const DynamicPrintC void Preset::set_visible_from_appconfig(const AppConfig &app_config) { if (vendor == nullptr) { return; } - const std::string &model = config.opt_string("printer_model"); - const std::string &variant = config.opt_string("printer_variant"); - if (model.empty() || variant.empty()) { return; } - is_visible = app_config.get_variant(vendor->id, model, variant); + + if (type == TYPE_PRINTER) { + const std::string &model = config.opt_string("printer_model"); + const std::string &variant = config.opt_string("printer_variant"); + if (model.empty() || variant.empty()) { return; } + is_visible = app_config.get_variant(vendor->id, model, variant); + } else if (type == TYPE_FILAMENT) { + is_visible = app_config.has("filaments", name); + } else if (type == TYPE_SLA_MATERIAL) { + is_visible = app_config.has("sla_materials", name); + } } const std::vector& Preset::print_options() @@ -404,7 +436,7 @@ const std::vector& Preset::filament_options() "filament_retract_length", "filament_retract_lift", "filament_retract_lift_above", "filament_retract_lift_below", "filament_retract_speed", "filament_deretract_speed", "filament_retract_restart_extra", "filament_retract_before_travel", "filament_retract_layer_change", "filament_wipe", "filament_retract_before_wipe", // Profile compatibility - "compatible_prints", "compatible_prints_condition", "compatible_printers", "compatible_printers_condition", "inherits" + "filament_vendor", "compatible_prints", "compatible_prints_condition", "compatible_printers", "compatible_printers_condition", "inherits" }; return s_opts; } @@ -501,11 +533,13 @@ const std::vector& Preset::sla_material_options() static std::vector s_opts; if (s_opts.empty()) { s_opts = { + "material_type", "initial_layer_height", "exposure_time", "initial_exposure_time", "material_correction", "material_notes", + "material_vendor", "default_sla_material_profile", "compatible_prints", "compatible_prints_condition", "compatible_printers", "compatible_printers_condition", "inherits" @@ -1054,7 +1088,9 @@ void PresetCollection::update_platter_ui(GUI::PresetComboBox *ui) bmps.emplace_back(m_bitmap_add ? *m_bitmap_add : wxNullBitmap); bmp = m_bitmap_cache->insert(bitmap_key, bmps); } - ui->set_label_marker(ui->Append(PresetCollection::separator(L("Add a new printer")), *bmp), GUI::PresetComboBox::LABEL_ITEM_CONFIG_WIZARD); + ui->set_label_marker(ui->Append(PresetCollection::separator(L("Add a new printer")), *bmp), GUI::PresetComboBox::LABEL_ITEM_WIZARD_PRINTERS); + } else if (m_type == Preset::TYPE_SLA_MATERIAL) { + ui->set_label_marker(ui->Append(PresetCollection::separator(L("Add/Remove materials")), wxNullBitmap), GUI::PresetComboBox::LABEL_ITEM_WIZARD_MATERIALS); } ui->SetSelection(selected_preset_item); @@ -1300,7 +1336,7 @@ bool PresetCollection::select_preset_by_name_strict(const std::string &name) } // Merge one vendor's presets with the other vendor's presets, report duplicates. -std::vector PresetCollection::merge_presets(PresetCollection &&other, const std::set &new_vendors) +std::vector PresetCollection::merge_presets(PresetCollection &&other, const VendorMap &new_vendors) { std::vector duplicates; for (Preset &preset : other.m_presets) { @@ -1311,9 +1347,9 @@ std::vector PresetCollection::merge_presets(PresetCollection &&othe if (it == m_presets.end() || it->name != preset.name) { if (preset.vendor != nullptr) { // Re-assign a pointer to the vendor structure in the new PresetBundle. - auto it = new_vendors.find(*preset.vendor); + auto it = new_vendors.find(preset.vendor->id); assert(it != new_vendors.end()); - preset.vendor = &(*it); + preset.vendor = &it->second; } this->m_presets.emplace(it, std::move(preset)); } else diff --git a/src/slic3r/GUI/Preset.hpp b/src/slic3r/GUI/Preset.hpp index 056b037009..6ed818719e 100644 --- a/src/slic3r/GUI/Preset.hpp +++ b/src/slic3r/GUI/Preset.hpp @@ -2,6 +2,8 @@ #define slic3r_Preset_hpp_ #include +#include +#include #include #include @@ -71,9 +73,14 @@ public: }; std::vector models; + std::set default_filaments; + std::set default_sla_materials; + VendorProfile() {} VendorProfile(std::string id) : id(std::move(id)) {} + // Load VendorProfile from an ini file. + // If `load_all` is false, only the header with basic info (name, version, URLs) is loaded. static VendorProfile from_ini(const boost::filesystem::path &path, bool load_all=true); static VendorProfile from_ini(const boost::property_tree::ptree &tree, const boost::filesystem::path &path, bool load_all=true); @@ -84,6 +91,12 @@ public: bool operator==(const VendorProfile &rhs) const { return this->id == rhs.id; } }; +// Note: it is imporant that map is used here rather than unordered_map, +// because we need iterators to not be invalidated, +// because Preset and the ConfigWizard hold pointers to VendorProfiles. +// XXX: maybe set is enough (cf. changes in Wizard) +typedef std::map VendorMap; + class Preset { public: @@ -433,7 +446,7 @@ protected: bool select_preset_by_name_strict(const std::string &name); // Merge one vendor's presets with the other vendor's presets, report duplicates. - std::vector merge_presets(PresetCollection &&other, const std::set &new_vendors); + std::vector merge_presets(PresetCollection &&other, const VendorMap &new_vendors); private: PresetCollection(); diff --git a/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp index 5785fd8507..92db623f02 100644 --- a/src/slic3r/GUI/PresetBundle.cpp +++ b/src/slic3r/GUI/PresetBundle.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -41,6 +42,8 @@ static std::vector s_project_options { "wiping_volumes_matrix" }; +const char *PresetBundle::PRUSA_BUNDLE = "PrusaResearch"; + PresetBundle::PresetBundle() : prints(Preset::TYPE_PRINT, Preset::print_options(), static_cast(FullPrintConfig::defaults())), filaments(Preset::TYPE_FILAMENT, Preset::filament_options(), static_cast(FullPrintConfig::defaults())), @@ -194,7 +197,7 @@ void PresetBundle::setup_directories() } } -void PresetBundle::load_presets(const AppConfig &config, const std::string &preferred_model_id) +void PresetBundle::load_presets(AppConfig &config, const std::string &preferred_model_id) { // First load the vendor specific system presets. std::string errors_cummulative = this->load_system_presets(); @@ -325,13 +328,71 @@ void PresetBundle::load_installed_printers(const AppConfig &config) } } +void PresetBundle::load_installed_filaments(AppConfig &config) +{ + if (! config.has_section(AppConfig::SECTION_FILAMENTS)) { + std::unordered_set comp_filaments; + + for (const Preset &printer : printers) { + if (! printer.is_visible || printer.printer_technology() != ptFFF) { + continue; + } + + for (const Preset &filament : filaments) { + if (filament.is_compatible_with_printer(printer)) { + comp_filaments.insert(&filament); + } + } + } + + for (const auto &filament: comp_filaments) { + config.set(AppConfig::SECTION_FILAMENTS, filament->name, "1"); + } + } + + for (auto &preset : filaments) { + preset.set_visible_from_appconfig(config); + } +} + +void PresetBundle::load_installed_sla_materials(AppConfig &config) +{ + if (! config.has_section(AppConfig::SECTION_MATERIALS)) { + std::unordered_set comp_sla_materials; + + for (const Preset &printer : printers) { + if (! printer.is_visible || printer.printer_technology() != ptSLA) { + continue; + } + + for (const Preset &material : sla_materials) { + if (material.is_compatible_with_printer(printer)) { + comp_sla_materials.insert(&material); + } + } + } + + for (const auto &material: comp_sla_materials) { + config.set(AppConfig::SECTION_MATERIALS, material->name, "1"); + } + } + + for (auto &preset : sla_materials) { + preset.set_visible_from_appconfig(config); + } +} + // Load selections (current print, current filaments, current printer) from config.ini // This is done on application start up or after updates are applied. -void PresetBundle::load_selections(const AppConfig &config, const std::string &preferred_model_id) +void PresetBundle::load_selections(AppConfig &config, const std::string &preferred_model_id) { // Update visibility of presets based on application vendor / model / variant configuration. this->load_installed_printers(config); + // Update visibility of filament and sla material presets + this->load_installed_filaments(config); + this->load_installed_sla_materials(config); + // Parse the initial print / filament / printer profile names. std::string initial_print_profile_name = remove_ini_suffix(config.get("presets", "print")); std::string initial_sla_print_profile_name = remove_ini_suffix(config.get("presets", "sla_print")); @@ -1032,9 +1093,9 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla auto vp = VendorProfile::from_ini(tree, path); if (vp.num_variants() == 0) return 0; - vendor_profile = &(*this->vendors.insert(vp).first); + vendor_profile = &this->vendors.insert({vp.id, vp}).first->second; } - + if (flags & LOAD_CFGBUNDLE_VENDOR_ONLY) { return 0; } @@ -1572,6 +1633,9 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, GUI::Pr selected_preset_item = ui->GetCount() - 1; } } + + ui->set_label_marker(ui->Append(PresetCollection::separator(L("Add/Remove filaments")), wxNullBitmap), GUI::PresetComboBox::LABEL_ITEM_WIZARD_FILAMENTS); + ui->SetSelection(selected_preset_item); ui->SetToolTip(ui->GetString(selected_preset_item)); ui->check_selection(); diff --git a/src/slic3r/GUI/PresetBundle.hpp b/src/slic3r/GUI/PresetBundle.hpp index f351f66ac3..b1010e07b0 100644 --- a/src/slic3r/GUI/PresetBundle.hpp +++ b/src/slic3r/GUI/PresetBundle.hpp @@ -4,7 +4,9 @@ #include "AppConfig.hpp" #include "Preset.hpp" +#include #include +#include #include class wxWindow; @@ -31,7 +33,7 @@ public: // Load ini files of all types (print, filament, printer) from Slic3r::data_dir() / presets. // Load selections (current print, current filaments, current printer) from config.ini // This is done just once on application start up. - void load_presets(const AppConfig &config, const std::string &preferred_model_id = ""); + void load_presets(AppConfig &config, const std::string &preferred_model_id = ""); // Export selections (current print, current filaments, current printer) into config.ini void export_selections(AppConfig &config); @@ -52,7 +54,8 @@ public: // There will be an entry for each system profile loaded, // and the system profiles will point to the VendorProfile instances owned by PresetBundle::vendors. - std::set vendors; + // std::set vendors; + VendorMap vendors; struct ObsoletePresets { std::vector prints; @@ -131,19 +134,25 @@ public: void load_default_preset_bitmaps(wxWindow *window); + // Set the is_visible flag for printer vendors, printer models and printer variants + // based on the user configuration. + // If the "vendor" section is missing, enable all models and variants of the particular vendor. + void load_installed_printers(const AppConfig &config); + + static const char *PRUSA_BUNDLE; private: std::string load_system_presets(); // Merge one vendor's presets with the other vendor's presets, report duplicates. std::vector merge_presets(PresetBundle &&other); - // Set the "enabled" flag for printer vendors, printer models and printer variants - // based on the user configuration. - // If the "vendor" section is missing, enable all models and variants of the particular vendor. - void load_installed_printers(const AppConfig &config); + // Set the is_visible flag for filaments and sla materials, + // apply defaults based on enabled printers when no filaments/materials are installed. + void load_installed_filaments(AppConfig &config); + void load_installed_sla_materials(AppConfig &config); // Load selections (current print, current filaments, current printer) from config.ini // This is done just once on application start up. - void load_selections(const AppConfig &config, const std::string &preferred_model_id = ""); + void load_selections(AppConfig &config, const std::string &preferred_model_id = ""); // Load print, filament & printer presets from a config. If it is an external config, then the name is extracted from the external path. // and the external config is just referenced, not stored into user profile directory. diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index dab1cfff12..c2a258e69b 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -227,9 +227,9 @@ void Tab::create_preset_tab() m_treectrl->Bind(wxEVT_KEY_DOWN, &Tab::OnKeyDown, this); m_presets_choice->Bind(wxEVT_COMBOBOX, ([this](wxCommandEvent e) { - //! Because of The MSW and GTK version of wxBitmapComboBox derived from wxComboBox, + //! Because of The MSW and GTK version of wxBitmapComboBox derived from wxComboBox, //! but the OSX version derived from wxOwnerDrawnCombo, instead of: - //! select_preset(m_presets_choice->GetStringSelection().ToUTF8().data()); + //! select_preset(m_presets_choice->GetStringSelection().ToUTF8().data()); //! we doing next: int selected_item = m_presets_choice->GetSelection(); if (m_selected_preset_item == size_t(selected_item) && !m_presets->current_is_dirty()) @@ -241,7 +241,7 @@ void Tab::create_preset_tab() selected_string == "------- User presets -------"*/) { m_presets_choice->SetSelection(m_selected_preset_item); if (wxString::FromUTF8(selected_string.c_str()) == PresetCollection::separator(L("Add a new printer"))) - wxTheApp->CallAfter([]() { Slic3r::GUI::config_wizard(Slic3r::GUI::ConfigWizard::RR_USER); }); + wxTheApp->CallAfter([]() { wxGetApp().run_wizard(ConfigWizard::RR_USER); }); return; } m_selected_preset_item = selected_item; diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index e0ee47b617..0109f6a290 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -455,6 +455,9 @@ Slic3r::GUI::BitmapCache* m_bitmap_cache = nullptr; std::vector bmps; std::vector colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); + if (bmps.empty()) + return bmps; + unsigned char rgb[3]; /* It's supposed that standard size of an icon is 36px*16px for 100% scaled display. @@ -484,6 +487,8 @@ static wxBitmap get_extruder_color_icon(size_t extruder_idx) { // Create the bitmap with color bars. std::vector bmps = get_extruder_color_icons(); + if (bmps.empty()) + return wxNullBitmap; return *bmps[extruder_idx >= bmps.size() ? 0 : extruder_idx]; } diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp index 5723afca2f..3cebf2f89e 100644 --- a/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -153,7 +153,7 @@ struct PresetUpdater::priv bool get_file(const std::string &url, const fs::path &target_path) const; void prune_tmps() const; void sync_version() const; - void sync_config(const std::set vendors); + void sync_config(const VendorMap vendors); void check_install_indices() const; Updates get_config_updates() const; @@ -266,7 +266,7 @@ void PresetUpdater::priv::sync_version() const // Download vendor indices. Also download new bundles if an index indicates there's a new one available. // Both are saved in cache. -void PresetUpdater::priv::sync_config(const std::set vendors) +void PresetUpdater::priv::sync_config(const VendorMap vendors) { BOOST_LOG_TRIVIAL(info) << "Syncing configuration cache"; @@ -276,13 +276,13 @@ void PresetUpdater::priv::sync_config(const std::set vendors) for (auto &index : index_db) { if (cancel) { return; } - const auto vendor_it = vendors.find(VendorProfile(index.vendor())); + const auto vendor_it = vendors.find(index.vendor()); if (vendor_it == vendors.end()) { BOOST_LOG_TRIVIAL(warning) << "No such vendor: " << index.vendor(); continue; } - const VendorProfile &vendor = *vendor_it; + const VendorProfile &vendor = vendor_it->second; if (vendor.config_update_url.empty()) { BOOST_LOG_TRIVIAL(info) << "Vendor has no config_update_url: " << vendor.name; continue; @@ -574,7 +574,7 @@ void PresetUpdater::sync(PresetBundle *preset_bundle) // Copy the whole vendors data for use in the background thread // Unfortunatelly as of C++11, it needs to be copied again // into the closure (but perhaps the compiler can elide this). - std::set vendors = preset_bundle->vendors; + VendorMap vendors = preset_bundle->vendors; p->thread = std::move(std::thread([this, vendors]() { this->p->prune_tmps(); @@ -643,13 +643,10 @@ PresetUpdater::UpdateResult PresetUpdater::config_update() const // (snapshot is taken beforehand) p->perform_updates(std::move(updates)); - GUI::ConfigWizard wizard(nullptr, GUI::ConfigWizard::RR_DATA_INCOMPAT); - - if (! wizard.run(GUI::wxGetApp().preset_bundle, this)) { + if (! GUI::wxGetApp().run_wizard(GUI::ConfigWizard::RR_DATA_INCOMPAT)) { return R_INCOMPAT_EXIT; } - GUI::wxGetApp().load_current_presets(); return R_INCOMPAT_CONFIGURED; } else { BOOST_LOG_TRIVIAL(info) << "User wants to exit Slic3r, bye..."; @@ -694,8 +691,8 @@ void PresetUpdater::install_bundles_rsrc(std::vector bundles, bool BOOST_LOG_TRIVIAL(info) << boost::format("Installing %1% bundles from resources ...") % bundles.size(); for (const auto &bundle : bundles) { - auto path_in_rsrc = p->rsrc_path / bundle; - auto path_in_vendors = p->vendor_path / bundle; + auto path_in_rsrc = (p->rsrc_path / bundle).replace_extension(".ini"); + auto path_in_vendors = (p->vendor_path / bundle).replace_extension(".ini"); updates.updates.emplace_back(std::move(path_in_rsrc), std::move(path_in_vendors), Version(), "", ""); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ef22327c9c..e2eaebfb80 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -11,7 +11,7 @@ add_library(Catch2::Catch2 ALIAS Catch2) include(Catch) add_library(test_catch2_common INTERFACE) -target_compile_definitions(test_catch2_common INTERFACE TEST_DATA_DIR=R"\(${TEST_DATA_DIR}\)") +target_compile_definitions(test_catch2_common INTERFACE TEST_DATA_DIR=R"\(${TEST_DATA_DIR}\)" CATCH_CONFIG_FAST_COMPILE) target_link_libraries(test_catch2_common INTERFACE Catch2::Catch2) add_library(test_common INTERFACE) @@ -22,10 +22,10 @@ endif() target_link_libraries(test_common INTERFACE test_catch2_common) # DEPRECATED: -find_package(GTest REQUIRED) -add_library(test_gtest_common INTERFACE) -target_compile_definitions(test_gtest_common INTERFACE TEST_DATA_DIR=R"\(${TEST_DATA_DIR}\)") -target_link_libraries(test_gtest_common INTERFACE GTest::GTest GTest::Main) +#find_package(GTest REQUIRED) +#add_library(test_gtest_common INTERFACE) +#target_compile_definitions(test_gtest_common INTERFACE TEST_DATA_DIR=R"\(${TEST_DATA_DIR}\)") +#target_link_libraries(test_gtest_common INTERFACE GTest::GTest GTest::Main) add_subdirectory(libnest2d) add_subdirectory(timeutils) diff --git a/tests/libnest2d/CMakeLists.txt b/tests/libnest2d/CMakeLists.txt index ac7debf933..ee38280b5f 100644 --- a/tests/libnest2d/CMakeLists.txt +++ b/tests/libnest2d/CMakeLists.txt @@ -2,4 +2,5 @@ get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests_main.cpp printer_parts.cpp printer_parts.hpp) target_link_libraries(${_TEST_NAME}_tests test_common libslic3r ${Boost_LIBRARIES} ${TBB_LIBRARIES} ${Boost_LIBRARIES}) -catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ") +# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ") +add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests "--durations yes") diff --git a/tests/libnest2d/libnest2d_tests_main.cpp b/tests/libnest2d/libnest2d_tests_main.cpp index 56f2f59c1a..1741bb8d64 100644 --- a/tests/libnest2d/libnest2d_tests_main.cpp +++ b/tests/libnest2d/libnest2d_tests_main.cpp @@ -1,5 +1,6 @@ #define CATCH_CONFIG_MAIN #include + #include #include @@ -224,12 +225,12 @@ TEST_CASE("Area", "[Geometry]") { using namespace libnest2d; RectangleItem rect(10, 10); - - REQUIRE(rect.area() == 100); + + REQUIRE(rect.area() == Approx(100)); RectangleItem rect2 = {100, 100}; - - REQUIRE(rect2.area() == 10000); + + REQUIRE(rect2.area() == Approx(10000)); Item item = { {61, 97}, @@ -288,11 +289,9 @@ TEST_CASE("IsPointInsidePolygon", "[Geometry]") { // REQUIRE_FALSE(ShapeLike::intersects(s1, s2)); //} -// Simple TEST_CASE, does not use gmock TEST_CASE("LeftAndDownPolygon", "[Geometry]") { using namespace libnest2d; - using namespace libnest2d; Box bin(100, 100); BottomLeftPlacer placer(bin); @@ -339,7 +338,6 @@ TEST_CASE("LeftAndDownPolygon", "[Geometry]") } } -// Simple TEST_CASE, does not use gmock TEST_CASE("ArrangeRectanglesTight", "[Nesting]") { using namespace libnest2d; @@ -449,8 +447,8 @@ TEST_CASE("ArrangeRectanglesLoose", "[Nesting]") [](const Item &i1, const Item &i2) { return i1.binId() < i2.binId(); }); - - size_t groups = max_group == rects.end() ? 0 : max_group->binId() + 1; + + auto groups = size_t(max_group == rects.end() ? 0 : max_group->binId() + 1); REQUIRE(groups == 1u); REQUIRE( @@ -477,8 +475,6 @@ using namespace libnest2d; template void exportSVG(std::vector>& result, const Bin& bin, int idx = 0) { - - std::string loc = "out"; static std::string svg_header = @@ -494,20 +490,20 @@ void exportSVG(std::vector>& result, const Bin& bin if(out.is_open()) { out << svg_header; Item rbin( RectangleItem(bin.width(), bin.height()) ); - for(unsigned i = 0; i < rbin.vertexCount(); i++) { - auto v = rbin.vertex(i); + for(unsigned j = 0; j < rbin.vertexCount(); j++) { + auto v = rbin.vertex(j); setY(v, -getY(v)/SCALE + 500 ); setX(v, getX(v)/SCALE); - rbin.setVertex(i, v); + rbin.setVertex(j, v); } out << shapelike::serialize(rbin.rawShape()) << std::endl; for(Item& sh : r) { Item tsh(sh.transformedShape()); - for(unsigned i = 0; i < tsh.vertexCount(); i++) { - auto v = tsh.vertex(i); + for(unsigned j = 0; j < tsh.vertexCount(); j++) { + auto v = tsh.vertex(j); setY(v, -getY(v)/SCALE + 500); setX(v, getX(v)/SCALE); - tsh.setVertex(i, v); + tsh.setVertex(j, v); } out << shapelike::serialize(tsh.rawShape()) << std::endl; } @@ -570,7 +566,7 @@ TEST_CASE("convexHull", "[Geometry]") { REQUIRE(chull.size() == poly.size()); } -TEST_CASE("NestPrusaPartsShouldFitIntoTwoBins", "[Nesting]") { +TEST_CASE("PrusaPartsShouldFitIntoTwoBins", "[Nesting]") { // Get the input items and define the bin. std::vector input = prusaParts(); @@ -579,21 +575,15 @@ TEST_CASE("NestPrusaPartsShouldFitIntoTwoBins", "[Nesting]") { // Do the nesting. Check in each step if the remaining items are less than // in the previous step. (Some algorithms can place more items in one step) size_t pcount = input.size(); - libnest2d::nest(input, bin, [&pcount](unsigned cnt) { - REQUIRE(cnt < pcount); - pcount = cnt; - }); - // Get the number of logical bins: search for the max binId... - auto max_binid_it = std::max_element(input.begin(), input.end(), - [](const Item &i1, const Item &i2) { - return i1.binId() < i2.binId(); - }); - - auto bins = size_t(max_binid_it == input.end() ? 0 : - max_binid_it->binId() + 1); + size_t bins = libnest2d::nest(input, bin, 0, {}, + ProgressFunction{[&pcount](unsigned cnt) { + REQUIRE(cnt < pcount); + pcount = cnt; + }}); // For prusa parts, 2 bins should be enough... + REQUIRE(bins > 0u); REQUIRE(bins <= 2u); // All parts should be processed by the algorithm @@ -617,29 +607,83 @@ TEST_CASE("NestPrusaPartsShouldFitIntoTwoBins", "[Nesting]") { } } -TEST_CASE("NestEmptyItemShouldBeUntouched", "[Nesting]") { +TEST_CASE("EmptyItemShouldBeUntouched", "[Nesting]") { auto bin = Box(250000000, 210000000); // dummy bin std::vector items; items.emplace_back(Item{}); // Emplace empty item items.emplace_back(Item{0, 200, 0}); // Emplace zero area item - libnest2d::nest(items, bin); - + size_t bins = libnest2d::nest(items, bin); + + REQUIRE(bins == 0u); for (auto &itm : items) REQUIRE(itm.binId() == BIN_ID_UNSET); } -TEST_CASE("NestLargeItemShouldBeUntouched", "[Nesting]") { +TEST_CASE("LargeItemShouldBeUntouched", "[Nesting]") { auto bin = Box(250000000, 210000000); // dummy bin std::vector items; items.emplace_back(RectangleItem{250000001, 210000001}); // Emplace large item - libnest2d::nest(items, bin); - + size_t bins = libnest2d::nest(items, bin); + + REQUIRE(bins == 0u); REQUIRE(items.front().binId() == BIN_ID_UNSET); } +TEST_CASE("Items can be preloaded", "[Nesting]") { + auto bin = Box({0, 0}, {250000000, 210000000}); // dummy bin + + std::vector items; + items.reserve(2); + + NestConfig<> cfg; + cfg.placer_config.alignment = NestConfig<>::Placement::Alignment::DONT_ALIGN; + + items.emplace_back(RectangleItem{10000000, 10000000}); + Item &fixed_rect = items.back(); + fixed_rect.translate(bin.center()); + + items.emplace_back(RectangleItem{20000000, 20000000}); + Item &movable_rect = items.back(); + movable_rect.translate(bin.center()); + + SECTION("Preloaded Item should be untouched") { + fixed_rect.markAsFixedInBin(0); + + size_t bins = libnest2d::nest(items, bin, 0, cfg); + + REQUIRE(bins == 1); + + REQUIRE(fixed_rect.binId() == 0); + REQUIRE(fixed_rect.translation().X == bin.center().X); + REQUIRE(fixed_rect.translation().Y == bin.center().Y); + + REQUIRE(movable_rect.binId() == 0); + REQUIRE(movable_rect.translation().X != bin.center().X); + REQUIRE(movable_rect.translation().Y != bin.center().Y); + } + + SECTION("Preloaded Item should not affect free bins") { + fixed_rect.markAsFixedInBin(1); + + size_t bins = libnest2d::nest(items, bin, 0, cfg); + + REQUIRE(bins == 2); + + REQUIRE(fixed_rect.binId() == 1); + REQUIRE(fixed_rect.translation().X == bin.center().X); + REQUIRE(fixed_rect.translation().Y == bin.center().Y); + + REQUIRE(movable_rect.binId() == 0); + + auto bb = movable_rect.boundingBox(); + REQUIRE(bb.center().X == bin.center().X); + REQUIRE(bb.center().Y == bin.center().Y); + } +} + namespace { struct ItemPair { @@ -969,8 +1013,8 @@ TEST_CASE("mergePileWithPolygon", "[Geometry]") { REQUIRE(result.size() == 1); RectangleItem ref(45, 15); - - REQUIRE(shapelike::area(result.front()) == ref.area()); + + REQUIRE(shapelike::area(result.front()) == Approx(ref.area())); } namespace { diff --git a/tests/sla_print/CMakeLists.txt b/tests/sla_print/CMakeLists.txt index 8df8abc8ef..687096ee4a 100644 --- a/tests/sla_print/CMakeLists.txt +++ b/tests/sla_print/CMakeLists.txt @@ -1,5 +1,6 @@ get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) -add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests.cpp tests_main.cpp) +add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests.cpp) target_link_libraries(${_TEST_NAME}_tests test_common libslic3r ${Boost_LIBRARIES} ${TBB_LIBRARIES} ${Boost_LIBRARIES}) -catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ") +#catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ") +add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests "--durations yes") diff --git a/tests/sla_print/sla_print_tests.cpp b/tests/sla_print/sla_print_tests.cpp index 69920eaa70..74b1ed8b1f 100644 --- a/tests/sla_print/sla_print_tests.cpp +++ b/tests/sla_print/sla_print_tests.cpp @@ -1,3 +1,4 @@ +#define CATCH_CONFIG_MAIN #include #include @@ -358,10 +359,13 @@ template void test_pairhash() const I Ibits = int(sizeof(I) * CHAR_BIT); const II IIbits = int(sizeof(II) * CHAR_BIT); - const int bits = IIbits / 2 < Ibits ? Ibits / 2 : Ibits; - + + int bits = IIbits / 2 < Ibits ? Ibits / 2 : Ibits; + if (std::is_signed::value) bits -= 1; + const I Imin = std::is_signed::value ? -I(std::pow(2., bits)) : 0; const I Imax = I(std::pow(2., bits) - 1); - std::uniform_int_distribution dis(0, Imax); + + std::uniform_int_distribution dis(Imin, Imax); for (size_t i = 0; i < nums;) { I a = dis(gen); @@ -395,6 +399,7 @@ template void test_pairhash() } TEST_CASE("Pillar pairhash should be unique", "[SLASupportGeneration]") { + test_pairhash(); test_pairhash(); test_pairhash(); test_pairhash(); diff --git a/tests/sla_print/tests_main.cpp b/tests/sla_print/tests_main.cpp deleted file mode 100644 index 4ed06df1f7..0000000000 --- a/tests/sla_print/tests_main.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#define CATCH_CONFIG_MAIN -#include diff --git a/tests/timeutils/CMakeLists.txt b/tests/timeutils/CMakeLists.txt index 95f5e37626..c4b7c2029d 100644 --- a/tests/timeutils/CMakeLists.txt +++ b/tests/timeutils/CMakeLists.txt @@ -2,4 +2,5 @@ get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests_main.cpp) target_link_libraries(${_TEST_NAME}_tests test_common libslic3r ${Boost_LIBRARIES} ${TBB_LIBRARIES} ${Boost_LIBRARIES}) -catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ") +# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ") +add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests "--durations yes") diff --git a/tests/timeutils/timeutils_tests_main.cpp b/tests/timeutils/timeutils_tests_main.cpp index 191ec6a863..c3827374e5 100644 --- a/tests/timeutils/timeutils_tests_main.cpp +++ b/tests/timeutils/timeutils_tests_main.cpp @@ -12,18 +12,18 @@ namespace { void test_time_fmt(Slic3r::Utils::TimeFormat fmt) { using namespace Slic3r::Utils; time_t t = get_current_time_utc(); - + std::string tstr = time2str(t, TimeZone::local, fmt); time_t parsedtime = str2time(tstr, TimeZone::local, fmt); REQUIRE(t == parsedtime); - + tstr = time2str(t, TimeZone::utc, fmt); parsedtime = str2time(tstr, TimeZone::utc, fmt); REQUIRE(t == parsedtime); - + parsedtime = str2time("not valid string", TimeZone::local, fmt); REQUIRE(parsedtime == time_t(-1)); - + parsedtime = str2time("not valid string", TimeZone::utc, fmt); REQUIRE(parsedtime == time_t(-1)); } @@ -31,8 +31,21 @@ void test_time_fmt(Slic3r::Utils::TimeFormat fmt) { TEST_CASE("ISO8601Z", "[Timeutils]") { test_time_fmt(Slic3r::Utils::TimeFormat::iso8601Z); + + std::string mydate = "20190710T085000Z"; + time_t t = Slic3r::Utils::parse_iso_utc_timestamp(mydate); + std::string date = Slic3r::Utils::iso_utc_timestamp(t); + + REQUIRE(date == mydate); } TEST_CASE("Slic3r_UTC_Time_Format", "[Timeutils]") { - test_time_fmt(Slic3r::Utils::TimeFormat::gcode); + using namespace Slic3r::Utils; + test_time_fmt(TimeFormat::gcode); + + std::string mydate = "2019-07-10 at 08:50:00 UTC"; + time_t t = Slic3r::Utils::str2time(mydate, TimeZone::utc, TimeFormat::gcode); + std::string date = Slic3r::Utils::utc_timestamp(t); + + REQUIRE(date == mydate); }