diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx index 6a1792bcff..4995bc9534 100644 --- a/resources/profiles/PrusaResearch.idx +++ b/resources/profiles/PrusaResearch.idx @@ -1,7 +1,9 @@ min_slic3r_version = 2.6.0-alpha1 +1.6.0-alpha2 Added profile for Prusament PETG Carbon Fiber and Fiberthree F3 PA-GF30 Pro. Updated acceleration settings for Prusa MINI. 1.6.0-alpha1 Updated FW version notification. Decreased min layer time for PLA. 1.6.0-alpha0 Default top fill set to monotonic lines. Updated infill/perimeter overlap values. Updated output filename format. Enabled dynamic overhang speeds. min_slic3r_version = 2.5.0-alpha0 +1.5.7 Added filament profile for Prusament PETG Carbon Fiber and Fiberthree F3 PA-GF30 Pro. 1.5.6 Updated FW version notification (MK2.5/MK3 family). Added filament profile for Kimya PEBA-S. 1.5.5 Added new Prusament Resin material profiles. Enabled g-code thumbnails for MK2.5 family printers. 1.5.4 Added material profiles for Prusament Resin BioBased60. diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index 5928e7a736..450ff0e92c 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -5,7 +5,7 @@ name = Prusa Research # Configuration version of this file. Config file will only be installed, if the config_version differs. # This means, the server may force the PrusaSlicer configuration to be downgraded. -config_version = 1.6.0-alpha1 +config_version = 1.6.0-alpha2 # Where to get the updates from? config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/ changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1% @@ -145,7 +145,6 @@ bridge_flow_ratio = 1 bridge_speed = 25 brim_width = 0 brim_separation = 0.1 -clip_multipart_objects = 1 compatible_printers = complete_objects = 0 default_acceleration = 1000 @@ -268,8 +267,10 @@ fill_pattern = grid travel_speed = 150 wipe_tower = 0 default_acceleration = 1000 -first_layer_acceleration = 800 -infill_acceleration = 1000 +first_layer_acceleration = 600 +infill_acceleration = 1500 +solid_infill_acceleration = 1500 +top_solid_infill_acceleration = 800 bridge_acceleration = 1000 support_material_speed = 40 max_print_speed = 150 @@ -373,7 +374,9 @@ fill_pattern = gyroid fill_density = 15% travel_speed = 150 perimeter_acceleration = 800 -infill_acceleration = 1000 +infill_acceleration = 1500 +solid_infill_acceleration = 1500 +top_solid_infill_acceleration = 800 bridge_acceleration = 1000 first_layer_acceleration = 800 default_acceleration = 1250 @@ -1283,6 +1286,10 @@ support_material_extrusion_width = 0.35 bridge_acceleration = 300 support_material_contact_distance = 0.1 raft_contact_distance = 0.1 +infill_acceleration = 1000 +solid_infill_acceleration = 1000 +top_solid_infill_acceleration = 800 +external_perimeter_acceleration = 300 [print:0.07mm ULTRADETAIL @MINI] inherits = *0.07mm*; *MINI* @@ -1298,6 +1305,10 @@ support_material_extrusion_width = 0.35 bridge_acceleration = 300 support_material_contact_distance = 0.1 raft_contact_distance = 0.1 +infill_acceleration = 1000 +solid_infill_acceleration = 1000 +top_solid_infill_acceleration = 800 +external_perimeter_acceleration = 300 [print:0.10mm DETAIL @MINI] inherits = *0.10mm*; *MINI* @@ -1314,6 +1325,11 @@ fill_pattern = gyroid fill_density = 15% perimeters = 3 support_material_xy_spacing = 60% +infill_acceleration = 1200 +solid_infill_acceleration = 1000 +top_solid_infill_acceleration = 800 +perimeter_acceleration = 700 +external_perimeter_acceleration = 600 [print:0.15mm QUALITY @MINI] inherits = *0.15mm*; *MINI* @@ -1326,6 +1342,8 @@ top_solid_infill_speed = 40 fill_pattern = gyroid fill_density = 15% support_material_xy_spacing = 60% +perimeter_acceleration = 900 +external_perimeter_acceleration = 800 [print:0.15mm SPEED @MINI] inherits = *0.15mm*; *MINI* @@ -1336,6 +1354,8 @@ infill_speed = 140 solid_infill_speed = 140 top_solid_infill_speed = 40 support_material_xy_spacing = 60% +perimeter_acceleration = 1000 +external_perimeter_acceleration = 800 [print:0.20mm QUALITY @MINI] inherits = *0.20mm*; *MINI* @@ -1348,6 +1368,8 @@ top_solid_infill_speed = 40 fill_pattern = gyroid fill_density = 15% support_material_xy_spacing = 60% +perimeter_acceleration = 900 +external_perimeter_acceleration = 800 [print:0.20mm SPEED @MINI] inherits = *0.20mm*; *MINI* @@ -1359,6 +1381,8 @@ max_print_speed = 150 solid_infill_speed = 140 top_solid_infill_speed = 40 support_material_xy_spacing = 60% +perimeter_acceleration = 1000 +external_perimeter_acceleration = 800 [print:0.25mm DRAFT @MINI] inherits = *0.25mm*; *MINI* @@ -1378,6 +1402,8 @@ top_infill_extrusion_width = 0.4 support_material_xy_spacing = 60% support_material_contact_distance = 0.2 raft_contact_distance = 0.2 +perimeter_acceleration = 1000 +external_perimeter_acceleration = 800 # MINI - 0.25mm nozzle @@ -1389,6 +1415,10 @@ fill_density = 20% support_material_speed = 30 support_material_contact_distance = 0.07 raft_contact_distance = 0.07 +infill_acceleration = 1000 +solid_infill_acceleration = 1000 +top_solid_infill_acceleration = 800 +external_perimeter_acceleration = 300 [print:0.07mm ULTRADETAIL @0.25 nozzle MINI] inherits = *0.07mm*; *0.25nozzle*; *MINI* @@ -1401,6 +1431,10 @@ fill_pattern = grid fill_density = 20% support_material_contact_distance = 0.07 raft_contact_distance = 0.07 +infill_acceleration = 1000 +solid_infill_acceleration = 1000 +top_solid_infill_acceleration = 800 +external_perimeter_acceleration = 300 [print:0.10mm DETAIL @0.25 nozzle MINI] inherits = *0.10mm*; *0.25nozzleMINI*; *MINI* @@ -1409,6 +1443,10 @@ fill_pattern = grid fill_density = 20% support_material_contact_distance = 0.07 raft_contact_distance = 0.07 +infill_acceleration = 1200 +solid_infill_acceleration = 1000 +top_solid_infill_acceleration = 800 +external_perimeter_acceleration = 500 [print:0.15mm QUALITY @0.25 nozzle MINI] inherits = *0.15mm*; *0.25nozzleMINI*; *MINI* @@ -1421,6 +1459,10 @@ perimeter_extrusion_width = 0.27 external_perimeter_extrusion_width = 0.27 infill_extrusion_width = 0.27 solid_infill_extrusion_width = 0.27 +infill_acceleration = 1500 +solid_infill_acceleration = 1000 +top_solid_infill_acceleration = 800 +external_perimeter_acceleration = 500 # MINI - 0.6mm nozzle @@ -1433,11 +1475,17 @@ max_print_speed = 100 perimeter_speed = 45 solid_infill_speed = 70 top_solid_infill_speed = 45 -infill_extrusion_width = 0.65 -solid_infill_extrusion_width = 0.65 +perimeter_extrusion_width = 0.6 +external_perimeter_extrusion_width = 0.6 +infill_extrusion_width = 0.6 +solid_infill_extrusion_width = 0.6 +top_infill_extrusion_width = 0.5 support_material_contact_distance = 0.22 raft_contact_distance = 0.22 bridge_flow_ratio = 1 +top_solid_infill_acceleration = 800 +perimeter_acceleration = 900 +external_perimeter_acceleration = 800 [print:0.20mm DETAIL @0.6 nozzle MINI] inherits = *0.20mm*; *0.6nozzleMINI* @@ -1448,11 +1496,16 @@ max_print_speed = 100 perimeter_speed = 45 solid_infill_speed = 70 top_solid_infill_speed = 45 -infill_extrusion_width = 0.65 -solid_infill_extrusion_width = 0.65 +perimeter_extrusion_width = 0.6 +external_perimeter_extrusion_width = 0.6 +infill_extrusion_width = 0.6 +solid_infill_extrusion_width = 0.6 +top_infill_extrusion_width = 0.5 support_material_contact_distance = 0.22 raft_contact_distance = 0.22 bridge_flow_ratio = 1 +perimeter_acceleration = 900 +external_perimeter_acceleration = 800 [print:0.30mm QUALITY @0.6 nozzle MINI] inherits = *0.30mm*; *0.6nozzleMINI* @@ -1465,9 +1518,12 @@ solid_infill_speed = 65 top_solid_infill_speed = 45 external_perimeter_extrusion_width = 0.68 perimeter_extrusion_width = 0.68 +top_infill_extrusion_width = 0.55 support_material_contact_distance = 0.25 raft_contact_distance = 0.25 bridge_flow_ratio = 1 +perimeter_acceleration = 900 +external_perimeter_acceleration = 800 [print:0.35mm SPEED @0.6 nozzle MINI] inherits = *0.35mm*; *0.6nozzleMINI* @@ -1483,6 +1539,8 @@ perimeter_extrusion_width = 0.68 support_material_contact_distance = 0.25 raft_contact_distance = 0.25 bridge_flow_ratio = 0.95 +perimeter_acceleration = 1000 +external_perimeter_acceleration = 800 [print:0.40mm DRAFT @0.6 nozzle MINI] inherits = *0.40mm*; *0.6nozzleMINI* @@ -1500,6 +1558,8 @@ solid_infill_extrusion_width = 0.68 support_material_contact_distance = 0.25 raft_contact_distance = 0.25 bridge_flow_ratio = 0.95 +perimeter_acceleration = 1000 +external_perimeter_acceleration = 800 # MINI - 0.8mm nozzle @@ -1508,13 +1568,17 @@ inherits = 0.30mm DETAIL @0.8 nozzle compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]==0.8 perimeter_speed = 35 external_perimeter_speed = 25 -infill_acceleration = 1000 infill_speed = 50 max_print_speed = 80 solid_infill_speed = 45 top_solid_infill_speed = 35 support_material_speed = 40 travel_speed = 150 +infill_acceleration = 1500 +solid_infill_acceleration = 1500 +top_solid_infill_acceleration = 800 +perimeter_acceleration = 900 +external_perimeter_acceleration = 800 [print:0.40mm QUALITY @0.8 nozzle MINI] inherits = 0.40mm QUALITY @0.8 nozzle @@ -1525,11 +1589,15 @@ solid_infill_speed = 40 top_solid_infill_speed = 30 support_material_speed = 40 travel_speed = 150 +infill_acceleration = 1500 +solid_infill_acceleration = 1500 +top_solid_infill_acceleration = 800 +perimeter_acceleration = 1000 +external_perimeter_acceleration = 800 [print:0.55mm DRAFT @0.8 nozzle MINI] inherits = 0.55mm DRAFT @0.8 nozzle compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]==0.8 -infill_acceleration = 1000 infill_speed = 40 solid_infill_speed = 40 support_material_speed = 35 @@ -1539,6 +1607,11 @@ top_solid_infill_speed = 28 external_perimeter_extrusion_width = 1 perimeter_extrusion_width = 1 travel_speed = 150 +infill_acceleration = 1500 +solid_infill_acceleration = 1500 +top_solid_infill_acceleration = 800 +perimeter_acceleration = 1000 +external_perimeter_acceleration = 800 # XXXXXXxxXXXXXXXXXXXXXX # XXX--- filament ---XXX @@ -3865,6 +3938,17 @@ filament_spool_weight = 201 filament_type = PETG compatible_printers_condition = nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +[filament:Prusament PETG Carbon Fiber] +inherits = Prusament PETG +filament_vendor = Prusa Polymers +first_layer_temperature = 260 +temperature = 265 +extrusion_multiplier = 1.03 +filament_cost = 54.99 +filament_density = 1.27 +filament_colour = #BBBBBB +compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) + [filament:Prusa PETG @0.6 nozzle] inherits = *PET06* renamed_from = "Prusa PET 0.6 nozzle"; "Prusa PETG 0.6 nozzle" @@ -3883,6 +3967,14 @@ filament_density = 1.27 filament_spool_weight = 201 filament_type = PETG +[filament:Prusament PETG Carbon Fiber @0.6 nozzle] +inherits = Prusament PETG @0.6 nozzle +first_layer_temperature = 260 +temperature = 265 +extrusion_multiplier = 1.03 +filament_cost = 54.99 +filament_colour = #BBBBBB + [filament:Filament PM PETG @0.6 nozzle] inherits = *PET06* renamed_from = "Plasty Mladec PETG @0.6 nozzle" @@ -3976,6 +4068,14 @@ filament_cost = 36.29 filament_density = 1.27 filament_spool_weight = 201 +[filament:Prusament PETG Carbon Fiber @MMU2] +inherits = Prusament PETG @MMU2 +first_layer_temperature = 260 +temperature = 260 +extrusion_multiplier = 1.03 +filament_cost = 54.99 +filament_colour = #BBBBBB + [filament:Generic PETG @MMU2 0.6 nozzle] inherits = *PET MMU2 06* renamed_from = "Generic PET MMU2 0.6 nozzle"; "Generic PETG MMU2 0.6 nozzle" @@ -3995,6 +4095,14 @@ filament_cost = 36.29 filament_density = 1.27 filament_spool_weight = 201 +[filament:Prusament PETG Carbon Fiber @MMU2 0.6 nozzle] +inherits = Prusament PETG @MMU2 0.6 nozzle +first_layer_temperature = 260 +temperature = 260 +extrusion_multiplier = 1.03 +filament_cost = 54.99 +filament_colour = #BBBBBB + [filament:Filament PM PETG @MMU2 0.6 nozzle] inherits = *PET MMU2 06* renamed_from = "Plasty Mladec PETG @MMU2 0.6 nozzle" @@ -4079,6 +4187,87 @@ bed_temperature = 30 filament_retract_length = 0 extrusion_multiplier = 1.16 +[filament:Print With Smile PLA] +inherits = *PLA* +filament_vendor = Print With Smile +filament_cost = 535 +filament_density = 1.24 +filament_spool_weight = 0 +filament_colour = #FFFF6F +filament_max_volumetric_speed = 15 +first_layer_temperature = 205 +temperature = 205 + +[filament:Print With Smile PETG] +inherits = *PET* +filament_vendor = Print With Smile +filament_cost = 590 +filament_density = 1.27 +filament_spool_weight = 0 +filament_colour = #4D9398 +filament_max_volumetric_speed = 8 +temperature = 245 +first_layer_bed_temperature = 85 +first_layer_temperature = 240 +bed_temperature = 90 + +[filament:Print With Smile PETG @MINI] +inherits = Print With Smile PETG; *PETMINI* + +[filament:Print With Smile ASA] +inherits = Ultrafuse ASA +filament_vendor = Print With Smile +filament_cost = 625 +first_layer_temperature = 250 +temperature = 250 +first_layer_bed_temperature = 105 +bed_temperature = 110 +filament_type = ASA +max_fan_speed = 40 +bridge_fan_speed = 70 +filament_max_volumetric_speed = 11 + +[filament:Print With Smile ABS] +inherits = *ABSC* +filament_vendor = Print With Smile +filament_cost = 535 +filament_density = 1.08 +first_layer_temperature = 240 +temperature = 240 + +[filament:Print With Smile PETG CF] +inherits = Extrudr XPETG CF +filament_vendor = Print With Smile +filament_cost = 899 +filament_density = 1.29 +filament_notes = +first_layer_temperature = 260 +temperature = 260 +first_layer_bed_temperature = 85 +bed_temperature = 85 +max_fan_speed = 55 +bridge_fan_speed = 60 +filament_max_volumetric_speed = 5 + +[filament:Print With Smile PETG CF @MINI] +inherits = Print With Smile PETG CF; *PETMINI* + +[filament:Print With Smile TPU96A] +inherits = *FLEX* +filament_vendor = Print With Smile +filament_cost = 1200 +filament_density = 1.31 +extrusion_multiplier = 1.1 +filament_max_volumetric_speed = 1.35 +fan_always_on = 1 +cooling = 0 +max_fan_speed = 60 +min_fan_speed = 60 +disable_fan_first_layers = 4 +full_fan_speed_layer = 6 +filament_retract_length = 1.2 +filament_deretract_speed = 20 + [filament:Fiberlogy Easy PLA] inherits = *PLA* renamed_from = Fiberlogy PLA @@ -4680,6 +4869,23 @@ fan_always_on = 1 max_fan_speed = 15 min_fan_speed = 15 +[filament:Fiberthree F3 PA-GF30 Pro] +inherits = Prusament PC Blend Carbon Fiber +filament_vendor = Fiberthree +filament_cost = 208.01 +filament_density = 1.35 +extrusion_multiplier = 1.03 +first_layer_temperature = 275 +temperature = 285 +first_layer_bed_temperature = 90 +bed_temperature = 90 +fan_below_layer_time = 10 +compatible_printers_condition = printer_model!="MINI" and ! single_extruder_multi_material +max_fan_speed = 15 +min_fan_speed = 15 +filament_type = PA +filament_max_volumetric_speed = 6 + [filament:Taulman T-Glase] inherits = *PET* filament_vendor = Taulman @@ -4889,6 +5095,13 @@ renamed_from = "Prusa PET MMU1"; "Prusa PETG MMU1" [filament:Prusament PETG @MMU1] inherits = Prusament PETG; *PETMMU1* +[filament:Prusament PETG Carbon Fiber @MMU1] +inherits = Prusament PETG @MMU1 +first_layer_temperature = 260 +temperature = 265 +filament_cost = 54.99 +filament_colour = #BBBBBB + [filament:Taulman T-Glase @MMU1] inherits = Taulman T-Glase; *PETMMU1* start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}30{endif}; Filament gcode" @@ -5016,6 +5229,19 @@ fan_always_on = 1 max_fan_speed = 15 min_fan_speed = 15 +[filament:Fiberthree F3 PA-GF30 Pro @MINI] +inherits = Fiberthree F3 PA-GF30 Pro +filament_vendor = Fiberthree +first_layer_temperature = 275 +temperature = 280 +compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model=="MINI" +filament_retract_length = nil +filament_retract_speed = nil +filament_retract_lift = nil +filament_retract_before_travel = nil +filament_wipe = nil +filament_type = PA + [filament:Kimya ABS Carbon @MINI] inherits = Kimya ABS Carbon; *ABSMINI* filament_max_volumetric_speed = 6 @@ -5043,6 +5269,15 @@ inherits = Verbatim ABS; *ABSMINI* inherits = Prusament PETG; *PETMINI* compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +[filament:Prusament PETG Carbon Fiber @MINI] +inherits = Prusament PETG @MINI +first_layer_temperature = 260 +temperature = 265 +extrusion_multiplier = 1.03 +filament_cost = 54.99 +filament_colour = #BBBBBB +compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 + [filament:Kimya PETG Carbon @MINI] inherits = Kimya PETG Carbon; *PETMINI* filament_max_volumetric_speed = 6 @@ -5053,6 +5288,14 @@ compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model=="MINI [filament:Prusament PETG @0.6 nozzle MINI] inherits = Prusament PETG; *PETMINI06* +[filament:Prusament PETG Carbon Fiber @0.6 nozzle MINI] +inherits = Prusament PETG @0.6 nozzle MINI +first_layer_temperature = 260 +temperature = 265 +extrusion_multiplier = 1.03 +filament_cost = 54.99 +filament_colour = #BBBBBB + [filament:Generic PETG @0.6 nozzle MINI] inherits = Generic PETG; *PETMINI06* renamed_from = "Generic PET 0.6 nozzle MINI"; "Generic PETG 0.6 nozzle MINI" @@ -5358,6 +5601,14 @@ filament_retract_lift = 0.2 slowdown_below_layer_time = 20 compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +[filament:Prusament PETG Carbon Fiber @0.8 nozzle] +inherits = Prusament PETG @0.8 nozzle +first_layer_temperature = 265 +temperature = 270 +extrusion_multiplier = 1.03 +filament_cost = 54.99 +filament_colour = #BBBBBB + [filament:Prusament ASA @0.8 nozzle] inherits = Prusament ASA first_layer_temperature = 265 @@ -5437,6 +5688,14 @@ temperature = 240 slowdown_below_layer_time = 20 filament_ramming_parameters = "120 140 5.51613 5.6129 5.70968 5.77419 5.77419 5.74194 5.80645 5.93548 6.06452 6.19355 6.3871 6.74194 7.25806 7.87097 8.54839 9.22581 10 10.8387| 0.05 5.5032 0.45 5.63868 0.95 5.8 1.45 5.7839 1.95 6.02257 2.45 6.25811 2.95 7.08395 3.45 8.43875 3.95 9.92258 4.45 11.3419 4.95 7.6" +[filament:Prusament PETG Carbon Fiber @MMU2 0.8 nozzle] +inherits = Prusament PETG @MMU2 0.8 nozzle +first_layer_temperature = 265 +temperature = 270 +extrusion_multiplier = 1.03 +filament_cost = 54.99 +filament_colour = #BBBBBB + [filament:Generic PLA @MMU2 0.8 nozzle] inherits = Generic PLA @MMU2 compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material @@ -5520,6 +5779,14 @@ filament_retract_lift = 0.25 slowdown_below_layer_time = 20 compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model=="MINI" +[filament:Prusament PETG Carbon Fiber @0.8 nozzle MINI] +inherits = Prusament PETG @0.8 nozzle MINI +first_layer_temperature = 265 +temperature = 270 +extrusion_multiplier = 1.03 +filament_cost = 54.99 +filament_colour = #BBBBBB + [filament:Prusament ASA @0.8 nozzle MINI] inherits = Prusament ASA @MINI first_layer_temperature = 265 @@ -10039,7 +10306,7 @@ default_filament_profile = "Prusament PLA" default_print_profile = 0.15mm QUALITY @MINI gcode_flavor = marlin2 machine_max_acceleration_e = 5000 -machine_max_acceleration_extruding = 1250 +machine_max_acceleration_extruding = 2000 machine_max_acceleration_retracting = 1250 machine_max_acceleration_travel = 2500 machine_max_acceleration_x = 2500 diff --git a/resources/profiles/Templates.idx b/resources/profiles/Templates.idx index 3edb4400fb..297aeb1fee 100644 --- a/resources/profiles/Templates.idx +++ b/resources/profiles/Templates.idx @@ -1,2 +1,3 @@ min_slic3r_version = 2.6.0-alpha0 +1.0.1 Added Prusament PETG Carbon Fiber, Fiberthree F3 PA-GF30 Pro. 1.0.0 Initial diff --git a/resources/profiles/Templates.ini b/resources/profiles/Templates.ini index 26b0c0f12e..cf7b8ebe8f 100644 --- a/resources/profiles/Templates.ini +++ b/resources/profiles/Templates.ini @@ -2,14 +2,12 @@ [vendor] name = Templates -config_version = 1.0.0 +config_version = 1.0.1 config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Templates/ templates_profile = 1 ## Generic filament profiles -# Print profiles for the Prusa Research printers. - [filament:*common*] cooling = 1 compatible_printers = @@ -1547,6 +1545,16 @@ filament_density = 1.27 filament_spool_weight = 201 filament_type = PETG +[filament:Prusament PETG Carbon Fiber] +inherits = Prusament PETG +filament_vendor = Prusa Polymers +first_layer_temperature = 260 +temperature = 265 +extrusion_multiplier = 1.03 +filament_cost = 54.99 +filament_density = 1.27 +filament_colour = #BBBBBB + [filament:Prusa PLA] inherits = *PLA* filament_vendor = Made for Prusa @@ -2055,6 +2063,21 @@ fan_always_on = 1 max_fan_speed = 15 min_fan_speed = 15 +[filament:Fiberthree F3 PA-GF30 Pro] +inherits = Prusament PC Blend Carbon Fiber +filament_vendor = Fiberthree +filament_cost = 208.01 +filament_density = 1.35 +extrusion_multiplier = 1.03 +first_layer_temperature = 275 +temperature = 285 +first_layer_bed_temperature = 90 +bed_temperature = 90 +fan_below_layer_time = 10 +max_fan_speed = 15 +min_fan_speed = 15 +filament_type = PA + [filament:Taulman T-Glase] inherits = *PET* filament_vendor = Taulman diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index c5bbccb245..e600f343ca 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -382,7 +382,7 @@ int CLI::run(int argc, char **argv) } else if (opt_key == "align_xy") { const Vec2d &p = m_config.option("align_xy")->value; for (auto &model : m_models) { - BoundingBoxf3 bb = model.bounding_box(); + BoundingBoxf3 bb = model.bounding_box_exact(); // this affects volumes: model.translate(-(bb.min.x() - p.x()), -(bb.min.y() - p.y()), -bb.min.z()); } @@ -423,7 +423,7 @@ int CLI::run(int argc, char **argv) } else if (opt_key == "cut" || opt_key == "cut_x" || opt_key == "cut_y") { std::vector new_models; for (auto &model : m_models) { - model.translate(0, 0, -model.bounding_box().min.z()); // align to z = 0 + model.translate(0, 0, -model.bounding_box_exact().min.z()); // align to z = 0 size_t num_objects = model.objects.size(); for (size_t i = 0; i < num_objects; ++ i) { diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index ac4af48283..2cc34be0e5 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -860,14 +860,26 @@ TransformationSVD::TransformationSVD(const Transform3d& trafo) rotation_90_degrees = true; for (int i = 0; i < 3; ++i) { const Vec3d row = v.row(i).cwiseAbs(); - size_t num_zeros = is_approx(row[0], 0.) + is_approx(row[1], 0.) + is_approx(row[2], 0.); - size_t num_ones = is_approx(row[0], 1.) + is_approx(row[1], 1.) + is_approx(row[2], 1.); + const size_t num_zeros = is_approx(row[0], 0.) + is_approx(row[1], 0.) + is_approx(row[2], 0.); + const size_t num_ones = is_approx(row[0], 1.) + is_approx(row[1], 1.) + is_approx(row[2], 1.); if (num_zeros != 2 || num_ones != 1) { rotation_90_degrees = false; break; } } - skew = ! rotation_90_degrees; + // Detect skew by brute force: check if the axes are still orthogonal after transformation + const Matrix3d trafo_linear = trafo.linear(); + const std::array axes = { Vec3d::UnitX(), Vec3d::UnitY(), Vec3d::UnitZ() }; + std::array transformed_axes; + for (int i = 0; i < 3; ++i) { + transformed_axes[i] = trafo_linear * axes[i]; + } + skew = std::abs(transformed_axes[0].dot(transformed_axes[1])) > EPSILON || + std::abs(transformed_axes[1].dot(transformed_axes[2])) > EPSILON || + std::abs(transformed_axes[2].dot(transformed_axes[0])) > EPSILON; + + // This following old code does not work under all conditions. The v matrix can become non diagonal (see SPE-1492) +// skew = ! rotation_90_degrees; } else skew = false; } @@ -918,4 +930,30 @@ double rotation_diff_z(const Transform3d &trafo_from, const Transform3d &trafo_t return atan2(vx.y(), vx.x()); } +bool trafos_differ_in_rotation_by_z_and_mirroring_by_xy_only(const Transform3d &t1, const Transform3d &t2) +{ + if (std::abs(t1.translation().z() - t2.translation().z()) > EPSILON) + // One of the object is higher than the other above the build plate (or below the build plate). + return false; + Matrix3d m1 = t1.matrix().block<3, 3>(0, 0); + Matrix3d m2 = t2.matrix().block<3, 3>(0, 0); + Matrix3d m = m2.inverse() * m1; + Vec3d z = m.block<3, 1>(0, 2); + if (std::abs(z.x()) > EPSILON || std::abs(z.y()) > EPSILON || std::abs(z.z() - 1.) > EPSILON) + // Z direction or length changed. + return false; + // Z still points in the same direction and it has the same length. + Vec3d x = m.block<3, 1>(0, 0); + Vec3d y = m.block<3, 1>(0, 1); + if (std::abs(x.z()) > EPSILON || std::abs(y.z()) > EPSILON) + return false; + double lx2 = x.squaredNorm(); + double ly2 = y.squaredNorm(); + if (lx2 - 1. > EPSILON * EPSILON || ly2 - 1. > EPSILON * EPSILON) + return false; + // Verify whether the vectors x, y are still perpendicular. + double d = x.dot(y); + return std::abs(d * d) < EPSILON * lx2 * ly2; +} + }} // namespace Slic3r::Geometry diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index 1f287f6a7f..ba7e7a4b2a 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -584,6 +584,13 @@ inline bool is_rotation_ninety_degrees(const Vec3d &rotation) return is_rotation_ninety_degrees(rotation.x()) && is_rotation_ninety_degrees(rotation.y()) && is_rotation_ninety_degrees(rotation.z()); } +// Returns true if one transformation may be converted into another transformation by +// rotation around Z and by mirroring in X / Y only. Two objects sharing such transformation +// may share support structures and they share Z height. +bool trafos_differ_in_rotation_by_z_and_mirroring_by_xy_only(const Transform3d &t1, const Transform3d &t2); +inline bool trafos_differ_in_rotation_by_z_and_mirroring_by_xy_only(const Transformation &t1, const Transformation &t2) + { return trafos_differ_in_rotation_by_z_and_mirroring_by_xy_only(t1.get_matrix(), t2.get_matrix()); } + template std::pair dir_to_spheric(const Vec<3, Tin> &n, Tout norm = 1.) { diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 01b4c5f6ff..dec734ca13 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -323,14 +323,30 @@ bool Model::add_default_instances() } // this returns the bounding box of the *transformed* instances -BoundingBoxf3 Model::bounding_box() const +BoundingBoxf3 Model::bounding_box_approx() const { BoundingBoxf3 bb; for (ModelObject *o : this->objects) - bb.merge(o->bounding_box()); + bb.merge(o->bounding_box_approx()); return bb; } +BoundingBoxf3 Model::bounding_box_exact() const +{ + BoundingBoxf3 bb; + for (ModelObject *o : this->objects) + bb.merge(o->bounding_box_exact()); + return bb; +} + +double Model::max_z() const +{ + double z = 0; + for (ModelObject *o : this->objects) + z = std::max(z, o->max_z()); + return z; +} + unsigned int Model::update_print_volume_state(const BuildVolume &build_volume) { unsigned int num_printable = 0; @@ -377,7 +393,7 @@ void Model::duplicate_objects_grid(size_t x, size_t y, coordf_t dist) ModelObject* object = this->objects.front(); object->clear_instances(); - Vec3d ext_size = object->bounding_box().size() + dist * Vec3d::Ones(); + Vec3d ext_size = object->bounding_box_exact().size() + dist * Vec3d::Ones(); for (size_t x_copy = 1; x_copy <= x; ++x_copy) { for (size_t y_copy = 1; y_copy <= y; ++y_copy) { @@ -548,13 +564,13 @@ void Model::adjust_min_z() if (objects.empty()) return; - if (bounding_box().min(2) < 0.0) + if (this->bounding_box_exact().min.z() < 0.0) { for (ModelObject* obj : objects) { if (obj != nullptr) { - coordf_t obj_min_z = obj->bounding_box().min(2); + coordf_t obj_min_z = obj->min_z(); if (obj_min_z < 0.0) obj->translate_instances(Vec3d(0.0, 0.0, -obj_min_z)); } @@ -627,12 +643,7 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs) this->printable = rhs.printable; this->origin_translation = rhs.origin_translation; this->cut_id.copy(rhs.cut_id); - m_bounding_box = rhs.m_bounding_box; - m_bounding_box_valid = rhs.m_bounding_box_valid; - m_raw_bounding_box = rhs.m_raw_bounding_box; - m_raw_bounding_box_valid = rhs.m_raw_bounding_box_valid; - m_raw_mesh_bounding_box = rhs.m_raw_mesh_bounding_box; - m_raw_mesh_bounding_box_valid = rhs.m_raw_mesh_bounding_box_valid; + this->copy_transformation_caches(rhs); this->clear_volumes(); this->volumes.reserve(rhs.volumes.size()); @@ -668,12 +679,7 @@ ModelObject& ModelObject::assign_copy(ModelObject &&rhs) this->layer_height_profile = std::move(rhs.layer_height_profile); this->printable = std::move(rhs.printable); this->origin_translation = std::move(rhs.origin_translation); - m_bounding_box = std::move(rhs.m_bounding_box); - m_bounding_box_valid = std::move(rhs.m_bounding_box_valid); - m_raw_bounding_box = rhs.m_raw_bounding_box; - m_raw_bounding_box_valid = rhs.m_raw_bounding_box_valid; - m_raw_mesh_bounding_box = rhs.m_raw_mesh_bounding_box; - m_raw_mesh_bounding_box_valid = rhs.m_raw_mesh_bounding_box_valid; + this->copy_transformation_caches(rhs); this->clear_volumes(); this->volumes = std::move(rhs.volumes); @@ -864,16 +870,73 @@ void ModelObject::clear_instances() // Returns the bounding box of the transformed instances. // This bounding box is approximate and not snug. -const BoundingBoxf3& ModelObject::bounding_box() const +const BoundingBoxf3& ModelObject::bounding_box_approx() const { - if (! m_bounding_box_valid) { - m_bounding_box_valid = true; + if (! m_bounding_box_approx_valid) { + m_bounding_box_approx_valid = true; BoundingBoxf3 raw_bbox = this->raw_mesh_bounding_box(); - m_bounding_box.reset(); + m_bounding_box_approx.reset(); for (const ModelInstance *i : this->instances) - m_bounding_box.merge(i->transform_bounding_box(raw_bbox)); + m_bounding_box_approx.merge(i->transform_bounding_box(raw_bbox)); + } + return m_bounding_box_approx; +} + +// Returns the bounding box of the transformed instances. +// This bounding box is approximate and not snug. +const BoundingBoxf3& ModelObject::bounding_box_exact() const +{ + if (! m_bounding_box_exact_valid) { + m_bounding_box_exact_valid = true; + m_min_max_z_valid = true; + BoundingBoxf3 raw_bbox = this->raw_mesh_bounding_box(); + m_bounding_box_exact.reset(); + for (size_t i = 0; i < this->instances.size(); ++ i) + m_bounding_box_exact.merge(this->instance_bounding_box(i)); + } + return m_bounding_box_exact; +} + +double ModelObject::min_z() const +{ + const_cast(this)->update_min_max_z(); + return m_bounding_box_exact.min.z(); +} + +double ModelObject::max_z() const +{ + const_cast(this)->update_min_max_z(); + return m_bounding_box_exact.max.z(); +} + +void ModelObject::update_min_max_z() +{ + assert(! this->instances.empty()); + if (! m_min_max_z_valid && ! this->instances.empty()) { + m_min_max_z_valid = true; + const Transform3d mat_instance = this->instances.front()->get_transformation().get_matrix(); + double global_min_z = std::numeric_limits::max(); + double global_max_z = - std::numeric_limits::max(); + for (const ModelVolume *v : this->volumes) + if (v->is_model_part()) { + const Transform3d m = mat_instance * v->get_matrix(); + const Vec3d row_z = m.linear().row(2).cast(); + const double shift_z = m.translation().z(); + double this_min_z = std::numeric_limits::max(); + double this_max_z = - std::numeric_limits::max(); + for (const Vec3f &p : v->mesh().its.vertices) { + double z = row_z.dot(p.cast()); + this_min_z = std::min(this_min_z, z); + this_max_z = std::max(this_max_z, z); + } + this_min_z += shift_z; + this_max_z += shift_z; + global_min_z = std::min(global_min_z, this_min_z); + global_max_z = std::max(global_max_z, this_max_z); + } + m_bounding_box_exact.min.z() = global_min_z; + m_bounding_box_exact.max.z() = global_max_z; } - return m_bounding_box; } // A mesh containing all transformed instances of this object. @@ -1031,19 +1094,19 @@ void ModelObject::ensure_on_bed(bool allow_negative_z) if (allow_negative_z) { if (parts_count() == 1) { - const double min_z = get_min_z(); - const double max_z = get_max_z(); + const double min_z = this->min_z(); + const double max_z = this->max_z(); if (min_z >= SINKING_Z_THRESHOLD || max_z < 0.0) z_offset = -min_z; } else { - const double max_z = get_max_z(); + const double max_z = this->max_z(); if (max_z < SINKING_MIN_Z_THRESHOLD) z_offset = SINKING_MIN_Z_THRESHOLD - max_z; } } else - z_offset = -get_min_z(); + z_offset = -this->min_z(); if (z_offset != 0.0) translate_instances(z_offset * Vec3d::UnitZ()); @@ -1070,8 +1133,10 @@ void ModelObject::translate(double x, double y, double z) v->translate(x, y, z); } - if (m_bounding_box_valid) - m_bounding_box.translate(x, y, z); + if (m_bounding_box_approx_valid) + m_bounding_box_approx.translate(x, y, z); + if (m_bounding_box_exact_valid) + m_bounding_box_exact.translate(x, y, z); } void ModelObject::scale(const Vec3d &versor) @@ -1866,32 +1931,6 @@ void ModelObject::bake_xy_rotation_into_meshes(size_t instance_idx) this->invalidate_bounding_box(); } -double ModelObject::get_min_z() const -{ - if (instances.empty()) - return 0.0; - else { - double min_z = DBL_MAX; - for (size_t i = 0; i < instances.size(); ++i) { - min_z = std::min(min_z, get_instance_min_z(i)); - } - return min_z; - } -} - -double ModelObject::get_max_z() const -{ - if (instances.empty()) - return 0.0; - else { - double max_z = -DBL_MAX; - for (size_t i = 0; i < instances.size(); ++i) { - max_z = std::max(max_z, get_instance_max_z(i)); - } - return max_z; - } -} - double ModelObject::get_instance_min_z(size_t instance_idx) const { double min_z = DBL_MAX; @@ -2268,7 +2307,7 @@ void ModelVolume::scale(const Vec3d& scaling_factors) void ModelObject::scale_to_fit(const Vec3d &size) { - Vec3d orig_size = this->bounding_box().size(); + Vec3d orig_size = this->bounding_box_exact().size(); double factor = std::min( size.x() / orig_size.x(), std::min( @@ -2373,37 +2412,6 @@ void ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) cons #endif // ENABLE_WORLD_COORDINATE } -BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh& mesh, bool dont_translate) const -{ - // Rotate around mesh origin. - TriangleMesh copy(mesh); -#if ENABLE_WORLD_COORDINATE - copy.transform(get_transformation().get_rotation_matrix()); -#else - copy.transform(get_matrix(true, false, true, true)); -#endif // ENABLE_WORLD_COORDINATE - BoundingBoxf3 bbox = copy.bounding_box(); - - if (!empty(bbox)) { - // Scale the bounding box along the three axes. - for (unsigned int i = 0; i < 3; ++i) - { - if (std::abs(get_scaling_factor((Axis)i)-1.0) > EPSILON) - { - bbox.min(i) *= get_scaling_factor((Axis)i); - bbox.max(i) *= get_scaling_factor((Axis)i); - } - } - - // Translate the bounding box. - if (! dont_translate) { - bbox.min += get_offset(); - bbox.max += get_offset(); - } - } - return bbox; -} - BoundingBoxf3 ModelInstance::transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate) const { #if ENABLE_WORLD_COORDINATE diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 2e0ec3e16d..08fa794810 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -168,7 +168,7 @@ private: friend class cereal::access; friend class UndoRedo::StackImpl; // Create an object for deserialization, don't allocate IDs for ModelMaterial and its config. - ModelMaterial() : ObjectBase(-1), config(-1), m_model(nullptr) { assert(this->id().invalid()); assert(this->config.id().invalid()); } + ModelMaterial() : ObjectBase(-1), config(-1) { assert(this->id().invalid()); assert(this->config.id().invalid()); } template void serialize(Archive &ar) { assert(this->id().invalid()); assert(this->config.id().invalid()); Internal::StaticSerializationWrapper config_wrapper(config); @@ -343,7 +343,7 @@ public: // The pairs of are packed into a 1D array. LayerHeightProfile layer_height_profile; // Whether or not this object is printable - bool printable; + bool printable { true }; // This vector holds position of selected support points for SLA. The data are // saved in mesh coordinates to allow using them for several instances. @@ -397,11 +397,22 @@ public: void delete_last_instance(); void clear_instances(); - // Returns the bounding box of the transformed instances. - // This bounding box is approximate and not snug. - // This bounding box is being cached. - const BoundingBoxf3& bounding_box() const; - void invalidate_bounding_box() { m_bounding_box_valid = false; m_raw_bounding_box_valid = false; m_raw_mesh_bounding_box_valid = false; } + // Returns the bounding box of the transformed instances. This bounding box is approximate and not snug, it is being cached. + const BoundingBoxf3& bounding_box_approx() const; + // Returns an exact bounding box of the transformed instances. The result it is being cached. + const BoundingBoxf3& bounding_box_exact() const; + // Return minimum / maximum of a printable object transformed into the world coordinate system. + // All instances share the same min / max Z. + double min_z() const; + double max_z() const; + + void invalidate_bounding_box() { + m_bounding_box_approx_valid = false; + m_bounding_box_exact_valid = false; + m_min_max_z_valid = false; + m_raw_bounding_box_valid = false; + m_raw_mesh_bounding_box_valid = false; + } // A mesh containing all transformed instances of this object. TriangleMesh mesh() const; @@ -477,8 +488,6 @@ public: // Rotation and mirroring is being baked in. In case the instance scaling was non-uniform, it is baked in as well. void bake_xy_rotation_into_meshes(size_t instance_idx); - double get_min_z() const; - double get_max_z() const; double get_instance_min_z(size_t instance_idx) const; double get_instance_max_z(size_t instance_idx) const; @@ -500,14 +509,13 @@ public: private: friend class Model; // This constructor assigns new ID to this ModelObject and its config. - explicit ModelObject(Model* model) : m_model(model), printable(true), origin_translation(Vec3d::Zero()), - m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) + explicit ModelObject(Model* model) : m_model(model), origin_translation(Vec3d::Zero()) { assert(this->id().valid()); assert(this->config.id().valid()); assert(this->layer_height_profile.id().valid()); } - explicit ModelObject(int) : ObjectBase(-1), config(-1), layer_height_profile(-1), m_model(nullptr), printable(true), origin_translation(Vec3d::Zero()), m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) + explicit ModelObject(int) : ObjectBase(-1), config(-1), layer_height_profile(-1), origin_translation(Vec3d::Zero()) { assert(this->id().invalid()); assert(this->config.id().invalid()); @@ -585,15 +593,31 @@ private: OBJECTBASE_DERIVED_COPY_MOVE_CLONE(ModelObject) // Parent object, owning this ModelObject. Set to nullptr here, so the macros above will have it initialized. - Model *m_model = nullptr; + Model *m_model { nullptr }; // Bounding box, cached. - mutable BoundingBoxf3 m_bounding_box; - mutable bool m_bounding_box_valid; + mutable BoundingBoxf3 m_bounding_box_approx; + mutable bool m_bounding_box_approx_valid { false }; + mutable BoundingBoxf3 m_bounding_box_exact; + mutable bool m_bounding_box_exact_valid { false }; + mutable bool m_min_max_z_valid { false }; mutable BoundingBoxf3 m_raw_bounding_box; - mutable bool m_raw_bounding_box_valid; + mutable bool m_raw_bounding_box_valid { false }; mutable BoundingBoxf3 m_raw_mesh_bounding_box; - mutable bool m_raw_mesh_bounding_box_valid; + mutable bool m_raw_mesh_bounding_box_valid { false }; + + // Only use this method if now the source and dest ModelObjects are equal, for example they were synchronized by Print::apply(). + void copy_transformation_caches(const ModelObject &src) { + m_bounding_box_approx = src.m_bounding_box_approx; + m_bounding_box_approx_valid = src.m_bounding_box_approx_valid; + m_bounding_box_exact = src.m_bounding_box_exact; + m_bounding_box_exact_valid = src.m_bounding_box_exact_valid; + m_min_max_z_valid = src.m_min_max_z_valid; + m_raw_bounding_box = src.m_raw_bounding_box; + m_raw_bounding_box_valid = src.m_raw_bounding_box_valid; + m_raw_mesh_bounding_box = src.m_raw_mesh_bounding_box; + m_raw_mesh_bounding_box_valid = src.m_raw_mesh_bounding_box_valid; + } // Called by Print::apply() to set the model pointer after making a copy. friend class Print; @@ -605,8 +629,7 @@ private: friend class UndoRedo::StackImpl; // Used for deserialization -> Don't allocate any IDs for the ModelObject or its config. ModelObject() : - ObjectBase(-1), config(-1), layer_height_profile(-1), - m_model(nullptr), m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) { + ObjectBase(-1), config(-1), layer_height_profile(-1) { assert(this->id().invalid()); assert(this->config.id().invalid()); assert(this->layer_height_profile.id().invalid()); @@ -617,12 +640,17 @@ private: Internal::StaticSerializationWrapper layer_heigth_profile_wrapper(layer_height_profile); ar(name, input_file, instances, volumes, config_wrapper, layer_config_ranges, layer_heigth_profile_wrapper, sla_support_points, sla_points_status, sla_drain_holes, printable, origin_translation, - m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid, + m_bounding_box_approx, m_bounding_box_approx_valid, + m_bounding_box_exact, m_bounding_box_exact_valid, m_min_max_z_valid, + m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid, cut_connectors, cut_id); } // Called by Print::validate() from the UI thread. unsigned int update_instances_print_volume_state(const BuildVolume &build_volume); + + // Called by min_z(), max_z() + void update_min_max_z(); }; enum class EnforcerBlockerType : int8_t { @@ -1106,7 +1134,7 @@ public: // flag showing the position of this instance with respect to the print volume (set by Print::validate() using ModelObject::check_instances_print_volume_state()) ModelInstanceEPrintVolumeState print_volume_state; // Whether or not this instance is printable - bool printable; + bool printable { true }; ModelObject* get_object() const { return this->object; } @@ -1156,9 +1184,7 @@ public: // To be called on an external mesh void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const; - // Calculate a bounding box of a transformed mesh. To be called on an external mesh. - BoundingBoxf3 transform_mesh_bounding_box(const TriangleMesh& mesh, bool dont_translate = false) const; - // Transform an external bounding box. + // Transform an external bounding box, thus the resulting bounding box is no more snug. BoundingBoxf3 transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate = false) const; // Transform an external vector. Vec3d transform_vector(const Vec3d& v, bool dont_translate = false) const; @@ -1201,7 +1227,7 @@ private: ModelObject* object; // Constructor, which assigns a new unique ID. - explicit ModelInstance(ModelObject* object) : print_volume_state(ModelInstancePVS_Inside), printable(true), object(object) { assert(this->id().valid()); } + explicit ModelInstance(ModelObject* object) : print_volume_state(ModelInstancePVS_Inside), object(object) { assert(this->id().valid()); } // Constructor, which assigns a new unique ID. explicit ModelInstance(ModelObject *object, const ModelInstance &other) : m_transformation(other.m_transformation), print_volume_state(ModelInstancePVS_Inside), printable(other.printable), object(object) { assert(this->id().valid() && this->id() != other.id()); } @@ -1316,8 +1342,12 @@ public: void delete_material(t_model_material_id material_id); void clear_materials(); bool add_default_instances(); - // Returns approximate axis aligned bounding box of this model - BoundingBoxf3 bounding_box() const; + // Returns approximate axis aligned bounding box of this model. + BoundingBoxf3 bounding_box_approx() const; + // Returns exact axis aligned bounding box of this model. + BoundingBoxf3 bounding_box_exact() const; + // Return maximum height of all printable objects. + double max_z() const; // Set the print_volume_state of PrintObject::instances, // return total number of printable objects. unsigned int update_print_volume_state(const BuildVolume &build_volume); diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index 109c949cd3..059cbdac4d 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -875,6 +875,38 @@ namespace client output.it_range = boost::iterator_range(opt.it_range.begin(), it_end); } + // Return a boolean value, true if the scalar variable referenced by "opt" is nullable and it has a nil value. + template + static void is_nil_test_scalar( + const MyContext *ctx, + OptWithPos &opt, + expr &output) + { + if (opt.opt->is_vector()) + ctx->throw_exception("Referencing a vector variable when scalar is expected", opt.it_range); + output.set_b(opt.opt->is_nil()); + output.it_range = opt.it_range; + } + + // Return a boolean value, true if an element of a vector variable referenced by "opt[index]" is nullable and it has a nil value. + template + static void is_nil_test_vector( + const MyContext *ctx, + OptWithPos &opt, + int &index, + Iterator it_end, + expr &output) + { + if (opt.opt->is_scalar()) + ctx->throw_exception("Referencing a scalar variable when vector is expected", opt.it_range); + const ConfigOptionVectorBase *vec = static_cast(opt.opt); + if (vec->empty()) + ctx->throw_exception("Indexing an empty vector variable", opt.it_range); + size_t idx = (index < 0) ? 0 : (index >= int(vec->size())) ? 0 : size_t(index); + output.set_b(static_cast(opt.opt)->is_nil(idx)); + output.it_range = boost::iterator_range(opt.it_range.begin(), it_end); + } + // Verify that the expression returns an integer, which may be used // to address a vector. template @@ -973,6 +1005,7 @@ namespace client { "unary_expression", "Expecting an expression." }, { "optional_parameter", "Expecting a closing brace or an optional parameter." }, { "scalar_variable_reference", "Expecting a scalar variable reference."}, + { "is_nil_test", "Expecting a scalar variable reference."}, { "variable_reference", "Expecting a variable reference."}, { "regular_expression", "Expecting a regular expression."} }; @@ -1259,6 +1292,7 @@ namespace client [ px::bind(&expr::template digits, _val, _2, _3) ] | (kw["int"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::to_int, _1, _val) ] | (kw["round"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::round, _1, _val) ] + | (kw["is_nil"] > '(' > is_nil_test(_r1) > ')') [_val = _1] | (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ] | (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ] | (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _1, _2, _val) ] @@ -1286,6 +1320,15 @@ namespace client [ px::bind(&MyContext::resolve_variable, _r1, _1, _val) ]; variable_reference.name("variable reference"); + is_nil_test = + variable_reference(_r1)[_a=_1] >> + ( + ('[' > additive_expression(_r1)[px::bind(&MyContext::evaluate_index, _1, _b)] > ']' > + iter_pos[px::bind(&MyContext::is_nil_test_vector, _r1, _a, _b, _1, _val)]) + | eps[px::bind(&MyContext::is_nil_test_scalar, _r1, _a, _val)] + ); + is_nil_test.name("is_nil test"); + regular_expression = raw[lexeme['/' > *((utf8char - char_('\\') - char_('/')) | ('\\' > char_)) > '/']]; regular_expression.name("regular_expression"); @@ -1295,6 +1338,7 @@ namespace client ("zdigits") ("if") ("int") + ("is_nil") //("inf") ("else") ("elsif") @@ -1329,6 +1373,7 @@ namespace client debug(optional_parameter); debug(scalar_variable_reference); debug(variable_reference); + debug(is_nil_test); debug(regular_expression); } } @@ -1374,6 +1419,8 @@ namespace client qi::rule(const MyContext*), qi::locals, int>, spirit_encoding::space_type> scalar_variable_reference; // Rule to translate an identifier to a ConfigOption, or to fail. qi::rule(const MyContext*), spirit_encoding::space_type> variable_reference; + // Evaluating whether a nullable variable is nil. + qi::rule(const MyContext*), qi::locals, int>, spirit_encoding::space_type> is_nil_test; qi::rule, spirit_encoding::space_type> if_else_output; // qi::rule, bool, std::string>, spirit_encoding::space_type> switch_output; diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index eebb4d54f8..55a643ea1f 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -879,7 +879,7 @@ std::pair PresetCollection::load_external_preset( // Insert a new profile. Preset &preset = this->load_preset(path, new_name, std::move(cfg), select == LoadAndSelect::Always); preset.is_external = true; - if (&this->get_selected_preset() == &preset) + if (this->m_idx_selected != size_t(-1) && &this->get_selected_preset() == &preset) this->get_edited_preset().is_external = true; return std::make_pair(&preset, false); diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index e137f0157b..ae5f18fcbc 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -1250,7 +1250,6 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ l->get_transformation().get_matrix().isApprox(r->get_transformation().get_matrix()); })) { // If some of the instances changed, the bounding box of the updated ModelObject is likely no more valid. // This is safe as the ModelObject's bounding box is only accessed from this function, which is called from the main thread only. - model_object.invalidate_bounding_box(); // Synchronize the content of instances. auto new_instance = model_object_new.instances.begin(); for (auto old_instance = model_object.instances.begin(); old_instance != model_object.instances.end(); ++ old_instance, ++ new_instance) { @@ -1259,6 +1258,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ (*old_instance)->printable = (*new_instance)->printable; } } + // Source / dest object share the same bounding boxes, just copy them. + model_object.copy_transformation_caches(model_object_new); } } diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 878acc10ad..76fddebc4d 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -3270,9 +3270,6 @@ void PrintConfigDef::init_sla_support_params(const std::string &prefix) { ConfigOptionDef* def; - constexpr const char * pretext_unavailable = L("Unavailable for this method.\n"); - std::string pretext; - def = this->add(prefix + "support_head_front_diameter", coFloat); def->label = L("Pinhead front diameter"); def->category = L("Supports"); @@ -3322,13 +3319,9 @@ void PrintConfigDef::init_sla_support_params(const std::string &prefix) def->mode = comExpert; def->set_default_value(new ConfigOptionPercent(50)); - pretext = ""; - if (prefix == "branching") - pretext = pretext_unavailable; - def = this->add(prefix + "support_max_bridges_on_pillar", coInt); def->label = L("Max bridges on a pillar"); - def->tooltip = pretext + L( + def->tooltip = L( "Maximum number of bridges that can be placed on a pillar. Bridges " "hold support point pinheads and connect to pillars as small branches."); def->min = 0; @@ -3336,14 +3329,10 @@ void PrintConfigDef::init_sla_support_params(const std::string &prefix) def->mode = comExpert; def->set_default_value(new ConfigOptionInt(prefix == "branching" ? 2 : 3)); - pretext = ""; - if (prefix.empty()) - pretext = pretext_unavailable; - def = this->add(prefix + "support_max_weight_on_model", coFloat); def->label = L("Max weight on model"); def->category = L("Supports"); - def->tooltip = pretext + L( + def->tooltip = L( "Maximum weight of sub-trees that terminate on the model instead of the print bed. The weight is the sum of the lenghts of all " "branches emanating from the endpoint."); def->sidetext = L("mm"); @@ -3351,13 +3340,9 @@ void PrintConfigDef::init_sla_support_params(const std::string &prefix) def->mode = comExpert; def->set_default_value(new ConfigOptionFloat(10.)); - pretext = ""; - if (prefix == "branching") - pretext = pretext_unavailable; - def = this->add(prefix + "support_pillar_connection_mode", coEnum); def->label = L("Pillar connection mode"); - def->tooltip = pretext + L("Controls the bridge type between two neighboring pillars." + def->tooltip = L("Controls the bridge type between two neighboring pillars." " Can be zig-zag, cross (double zig-zag) or dynamic which" " will automatically switch between the first two depending" " on the distance of the two pillars."); @@ -3378,11 +3363,7 @@ void PrintConfigDef::init_sla_support_params(const std::string &prefix) def->label = L("Pillar widening factor"); def->category = L("Supports"); - pretext = ""; - if (prefix.empty()) - pretext = pretext_unavailable; - - def->tooltip = pretext + + def->tooltip = L("Merging bridges or pillars into another pillars can " "increase the radius. Zero means no increase, one means " "full increase. The exact amount of increase is unspecified and can " @@ -3449,14 +3430,10 @@ void PrintConfigDef::init_sla_support_params(const std::string &prefix) def->set_default_value(new ConfigOptionFloat(default_val)); - pretext = ""; - if (prefix == "branching") - pretext = pretext_unavailable; - def = this->add(prefix + "support_max_pillar_link_distance", coFloat); def->label = L("Max pillar linking distance"); def->category = L("Supports"); - def->tooltip = pretext + L("The max distance of two pillars to get linked with each other." + def->tooltip = L("The max distance of two pillars to get linked with each other." " A zero value will prohibit pillar cascading."); def->sidetext = L("mm"); def->min = 0; // 0 means no linking diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 14c76fefe5..efdcb4fc64 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -77,7 +77,7 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, const Transfor Vec3d bbox_center = bbox.center(); // We may need to rotate the bbox / bbox_center from the original instance to the current instance. double z_diff = Geometry::rotation_diff_z(model_object->instances.front()->get_matrix(), instances.front().model_instance->get_matrix()); - if (std::abs(z_diff) > EPSILON) { + if (std::abs(z_diff) > EPSILON) { auto z_rot = Eigen::AngleAxisd(z_diff, Vec3d::UnitZ()); bbox = bbox.transformed(Transform3d(z_rot)); bbox_center = (z_rot * bbox_center).eval(); @@ -87,6 +87,7 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, const Transfor m_center_offset = Point::new_scale(bbox_center.x(), bbox_center.y()); // Size of the transformed mesh. This bounding may not be snug in XY plane, but it is snug in Z. m_size = (bbox.size() * (1. / SCALING_FACTOR)).cast(); + m_size.z() = model_object->max_z(); this->set_instances(std::move(instances)); } @@ -1736,7 +1737,7 @@ void PrintObject::update_slicing_parameters() { if (!m_slicing_params.valid) m_slicing_params = SlicingParameters::create_from_config( - this->print()->config(), m_config, this->model_object()->bounding_box().max.z(), this->object_extruders()); + this->print()->config(), m_config, this->model_object()->max_z(), this->object_extruders()); } SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full_config, const ModelObject& model_object, float object_max_z) diff --git a/src/libslic3r/SLA/SupportTreeUtilsLegacy.hpp b/src/libslic3r/SLA/SupportTreeUtilsLegacy.hpp index b504d82fb3..f8e2f3024f 100644 --- a/src/libslic3r/SLA/SupportTreeUtilsLegacy.hpp +++ b/src/libslic3r/SLA/SupportTreeUtilsLegacy.hpp @@ -141,6 +141,7 @@ std::pair create_ground_pillar( if (head_id >= 0) builder.head(head_id).bridge_id = br.id; endp = diffbr->endp; radius = diffbr->end_r; + end_radius = diffbr->end_r; builder.add_junction(endp, radius); non_head = true; dir = diffbr->get_dir(); diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index d3ff0a8d1d..6be62c4b39 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -2954,7 +2954,7 @@ SupportGeneratorLayersPtr generate_raft_base( Polygons columns; Polygons first_layer; if (columns_base != nullptr) { - if (columns_base->print_z > slicing_params.raft_contact_top_z - EPSILON) { + if (columns_base->bottom_print_z() > slicing_params.raft_interface_top_z - EPSILON) { // Classic supports with colums above the raft interface. base = columns_base->polygons; columns = base; diff --git a/src/libslic3r/TreeSupport.cpp b/src/libslic3r/TreeSupport.cpp index b61ffc31cb..532cf7553c 100644 --- a/src/libslic3r/TreeSupport.cpp +++ b/src/libslic3r/TreeSupport.cpp @@ -130,8 +130,10 @@ TreeSupportSettings::TreeSupportSettings(const TreeSupportMeshGroupSettings& mes this->raft_layers.emplace_back(z); } // Raft contact layer - z = slicing_params.raft_contact_top_z; - this->raft_layers.emplace_back(z); + if (slicing_params.raft_layers() > 1) { + z = slicing_params.raft_contact_top_z; + this->raft_layers.emplace_back(z); + } if (double dist_to_go = slicing_params.object_print_z_min - z; dist_to_go > EPSILON) { // Layers between the raft contacts and bottom of the object. auto nsteps = int(ceil(dist_to_go / slicing_params.max_suport_layer_height)); @@ -991,6 +993,30 @@ inline SupportGeneratorLayer& layer_allocate( return layer_initialize(layer_storage.back(), layer_type, slicing_params, config, layer_idx); } +int generate_raft_contact( + const PrintObject &print_object, + const TreeSupportSettings &config, + SupportGeneratorLayersPtr &top_contacts, + SupportGeneratorLayerStorage &layer_storage) +{ + int raft_contact_layer_idx = -1; + if (print_object.has_raft() && print_object.layer_count() > 0) { + // Produce raft contact layer outside of the tree support loop, so that no trees will be generated for the raft contact layer. + // Raft layers supporting raft contact interface will be produced by the classic raft generator. + // Find the raft contact layer. + raft_contact_layer_idx = int(config.raft_layers.size()) - 1; + while (raft_contact_layer_idx > 0 && config.raft_layers[raft_contact_layer_idx] > print_object.slicing_parameters().raft_contact_top_z + EPSILON) + -- raft_contact_layer_idx; + // Create the raft contact layer. + SupportGeneratorLayer &raft_contact_layer = layer_allocate(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), config, raft_contact_layer_idx); + top_contacts[raft_contact_layer_idx] = &raft_contact_layer; + const ExPolygons &lslices = print_object.get_layer(0)->lslices; + double expansion = print_object.config().raft_expansion.value; + raft_contact_layer.polygons = expansion > 0 ? expand(lslices, scaled(expansion)) : to_polygons(lslices); + } + return raft_contact_layer_idx; +} + using SupportElements = std::deque; /*! * \brief Creates the initial influence areas (that can later be propagated down) by placing them below the overhang. @@ -1064,24 +1090,7 @@ static void generate_initial_areas( const size_t num_raft_layers = config.raft_layers.size(); const size_t num_support_layers = size_t(std::max(0, int(print_object.layer_count()) + int(num_raft_layers) - int(z_distance_delta))); const size_t first_support_layer = std::max(int(num_raft_layers) - int(z_distance_delta), 1); - size_t first_tree_layer = 0; - - size_t raft_contact_layer_idx = std::numeric_limits::max(); - if (num_raft_layers > 0 && print_object.layer_count() > 0) { - // Produce raft contact layer outside of the tree support loop, so that no trees will be generated for the raft contact layer. - // Raft layers supporting raft contact interface will be produced by the classic raft generator. - // Find the raft contact layer. - raft_contact_layer_idx = config.raft_layers.size() - 1; - while (raft_contact_layer_idx > 0 && config.raft_layers[raft_contact_layer_idx] > print_object.slicing_parameters().raft_contact_top_z + EPSILON) - -- raft_contact_layer_idx; - // Create the raft contact layer. - SupportGeneratorLayer &raft_contact_layer = layer_allocate(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), config, raft_contact_layer_idx); - top_contacts[raft_contact_layer_idx] = &raft_contact_layer; - const ExPolygons &lslices = print_object.get_layer(0)->lslices; - double expansion = print_object.config().raft_expansion.value; - raft_contact_layer.polygons = expansion > 0 ? expand(lslices, scaled(expansion)) : to_polygons(lslices); - first_tree_layer = print_object.slicing_parameters().raft_layers() - 1; - } + const int raft_contact_layer_idx = generate_raft_contact(print_object, config, top_contacts, layer_storage); std::mutex mutex_layer_storage, mutex_movebounds; std::vector> already_inserted(num_support_layers); @@ -1434,47 +1443,50 @@ static void generate_initial_areas( } }); - // Remove tree tips that start below the raft contact, - // remove interface layers below the raft contact. - for (size_t i = 0; i < first_tree_layer; ++i) { - top_contacts[i] = nullptr; - move_bounds[i].clear(); - } - if (raft_contact_layer_idx != std::numeric_limits::max() && print_object.config().raft_expansion.value > 0) { - // If any tips at first_tree_layer now are completely inside the expanded raft layer, remove them as well before they are propagated to the ground. - Polygons &raft_polygons = top_contacts[raft_contact_layer_idx]->polygons; - EdgeGrid::Grid grid(get_extents(raft_polygons).inflated(SCALED_EPSILON)); - grid.create(raft_polygons, Polylines{}, coord_t(scale_(10.))); - SupportElements &first_layer_move_bounds = move_bounds[first_tree_layer]; - double threshold = scaled(print_object.config().raft_expansion.value) * 2.; - first_layer_move_bounds.erase(std::remove_if(first_layer_move_bounds.begin(), first_layer_move_bounds.end(), - [&grid, threshold](const SupportElement &el) { - coordf_t dist; - if (grid.signed_distance_edges(el.state.result_on_layer, threshold, dist)) { - assert(std::abs(dist) < threshold + SCALED_EPSILON); - // Support point is inside the expanded raft, remove it. - return dist < - 0.; - } - return false; - }), first_layer_move_bounds.end()); -#if 0 - // Remove the remaining tips from the raft: Closing operation on tip circles. - if (! first_layer_move_bounds.empty()) { - const double eps = 0.1; - // All tips supporting this layer are expected to have the same radius. - double radius = config.getRadius(first_layer_move_bounds.front().state); - // Connect the tips with the following closing radius. - double closing_distance = radius; - Polygon circle = make_circle(radius + closing_distance, eps); - Polygons circles; - circles.reserve(first_layer_move_bounds.size()); - for (const SupportElement &el : first_layer_move_bounds) { - circles.emplace_back(circle); - circles.back().translate(el.state.result_on_layer); - } - raft_polygons = diff(raft_polygons, offset(union_(circles), - closing_distance)); + if (raft_contact_layer_idx >= 0) { + const size_t first_tree_layer = print_object.slicing_parameters().raft_layers() - 1; + // Remove tree tips that start below the raft contact, + // remove interface layers below the raft contact. + for (size_t i = 0; i < first_tree_layer; ++i) { + top_contacts[i] = nullptr; + move_bounds[i].clear(); + } + if (raft_contact_layer_idx >= 0 && print_object.config().raft_expansion.value > 0) { + // If any tips at first_tree_layer now are completely inside the expanded raft layer, remove them as well before they are propagated to the ground. + Polygons &raft_polygons = top_contacts[raft_contact_layer_idx]->polygons; + EdgeGrid::Grid grid(get_extents(raft_polygons).inflated(SCALED_EPSILON)); + grid.create(raft_polygons, Polylines{}, coord_t(scale_(10.))); + SupportElements &first_layer_move_bounds = move_bounds[first_tree_layer]; + double threshold = scaled(print_object.config().raft_expansion.value) * 2.; + first_layer_move_bounds.erase(std::remove_if(first_layer_move_bounds.begin(), first_layer_move_bounds.end(), + [&grid, threshold](const SupportElement &el) { + coordf_t dist; + if (grid.signed_distance_edges(el.state.result_on_layer, threshold, dist)) { + assert(std::abs(dist) < threshold + SCALED_EPSILON); + // Support point is inside the expanded raft, remove it. + return dist < - 0.; + } + return false; + }), first_layer_move_bounds.end()); + #if 0 + // Remove the remaining tips from the raft: Closing operation on tip circles. + if (! first_layer_move_bounds.empty()) { + const double eps = 0.1; + // All tips supporting this layer are expected to have the same radius. + double radius = config.getRadius(first_layer_move_bounds.front().state); + // Connect the tips with the following closing radius. + double closing_distance = radius; + Polygon circle = make_circle(radius + closing_distance, eps); + Polygons circles; + circles.reserve(first_layer_move_bounds.size()); + for (const SupportElement &el : first_layer_move_bounds) { + circles.emplace_back(circle); + circles.back().translate(el.state.result_on_layer); + } + raft_polygons = diff(raft_polygons, offset(union_(circles), - closing_distance)); + } + #endif } -#endif } } @@ -4201,79 +4213,90 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume std::vector overhangs = generate_overhangs(config, *print.get_object(processing.second.front()), throw_on_cancel); // ### Precalculate avoidances, collision etc. - size_t num_support_layers = precalculate(print, overhangs, processing.first, processing.second, volumes, throw_on_cancel); - if (num_support_layers == 0) - continue; - - auto t_precalc = std::chrono::high_resolution_clock::now(); - - // value is the area where support may be placed. As this is calculated in CreateLayerPathing it is saved and reused in draw_areas - std::vector move_bounds(num_support_layers); - - // ### Place tips of the support tree - SupportGeneratorLayersPtr bottom_contacts(num_support_layers, nullptr); - SupportGeneratorLayersPtr top_contacts(num_support_layers, nullptr); - SupportGeneratorLayersPtr top_interface_layers(num_support_layers, nullptr); - SupportGeneratorLayersPtr intermediate_layers(num_support_layers, nullptr); + SupportGeneratorLayerStorage layer_storage; + SupportGeneratorLayersPtr top_contacts; + SupportGeneratorLayersPtr bottom_contacts; + SupportGeneratorLayersPtr top_interface_layers; + SupportGeneratorLayersPtr intermediate_layers; - for (size_t mesh_idx : processing.second) - generate_initial_areas(*print.get_object(mesh_idx), volumes, config, overhangs, move_bounds, top_contacts, top_interface_layers, layer_storage, throw_on_cancel); - auto t_gen = std::chrono::high_resolution_clock::now(); + if (size_t num_support_layers = precalculate(print, overhangs, processing.first, processing.second, volumes, throw_on_cancel); + num_support_layers > 0) { -#ifdef TREESUPPORT_DEBUG_SVG - for (size_t layer_idx = 0; layer_idx < move_bounds.size(); ++layer_idx) { - Polygons polys; - for (auto& area : move_bounds[layer_idx]) - append(polys, area.influence_area); - if (auto begin = move_bounds[layer_idx].begin(); begin != move_bounds[layer_idx].end()) - SVG::export_expolygons(debug_out_path("treesupport-initial_areas-%d.svg", layer_idx), - { { { union_ex(volumes.getWallRestriction(config.getCollisionRadius(begin->state), layer_idx, begin->state.use_min_xy_dist)) }, - { "wall_restricrictions", "gray", 0.5f } }, - { { union_ex(polys) }, { "parent", "red", "black", "", scaled(0.1f), 0.5f } } }); + auto t_precalc = std::chrono::high_resolution_clock::now(); + + // value is the area where support may be placed. As this is calculated in CreateLayerPathing it is saved and reused in draw_areas + std::vector move_bounds(num_support_layers); + + // ### Place tips of the support tree + top_contacts .assign(num_support_layers, nullptr); + bottom_contacts .assign(num_support_layers, nullptr); + top_interface_layers.assign(num_support_layers, nullptr); + intermediate_layers .assign(num_support_layers, nullptr); + + for (size_t mesh_idx : processing.second) + generate_initial_areas(*print.get_object(mesh_idx), volumes, config, overhangs, move_bounds, top_contacts, top_interface_layers, layer_storage, throw_on_cancel); + auto t_gen = std::chrono::high_resolution_clock::now(); + + #ifdef TREESUPPORT_DEBUG_SVG + for (size_t layer_idx = 0; layer_idx < move_bounds.size(); ++layer_idx) { + Polygons polys; + for (auto& area : move_bounds[layer_idx]) + append(polys, area.influence_area); + if (auto begin = move_bounds[layer_idx].begin(); begin != move_bounds[layer_idx].end()) + SVG::export_expolygons(debug_out_path("treesupport-initial_areas-%d.svg", layer_idx), + { { { union_ex(volumes.getWallRestriction(config.getCollisionRadius(begin->state), layer_idx, begin->state.use_min_xy_dist)) }, + { "wall_restricrictions", "gray", 0.5f } }, + { { union_ex(polys) }, { "parent", "red", "black", "", scaled(0.1f), 0.5f } } }); + } + #endif // TREESUPPORT_DEBUG_SVG + + // ### Propagate the influence areas downwards. This is an inherently serial operation. + create_layer_pathing(volumes, config, move_bounds, throw_on_cancel); + auto t_path = std::chrono::high_resolution_clock::now(); + + // ### Set a point in each influence area + create_nodes_from_area(volumes, config, move_bounds, throw_on_cancel); + auto t_place = std::chrono::high_resolution_clock::now(); + + // ### draw these points as circles + + if (print_object.config().support_material_style == smsTree) + draw_areas(*print.get_object(processing.second.front()), volumes, config, overhangs, move_bounds, + bottom_contacts, top_contacts, intermediate_layers, layer_storage, throw_on_cancel); + else { + assert(print_object.config().support_material_style == smsOrganic); + indexed_triangle_set branches = draw_branches(*print.get_object(processing.second.front()), volumes, config, move_bounds, throw_on_cancel); + // Reduce memory footprint. After this point only slice_branches() will use volumes and from that only collisions with zero radius will be used. + volumes.clear_all_but_object_collision(); + slice_branches(*print.get_object(processing.second.front()), volumes, config, overhangs, move_bounds, branches, + bottom_contacts, top_contacts, intermediate_layers, layer_storage, throw_on_cancel); + } + + auto t_draw = std::chrono::high_resolution_clock::now(); + auto dur_pre_gen = 0.001 * std::chrono::duration_cast(t_precalc - t_start).count(); + auto dur_gen = 0.001 * std::chrono::duration_cast(t_gen - t_precalc).count(); + auto dur_path = 0.001 * std::chrono::duration_cast(t_path - t_gen).count(); + auto dur_place = 0.001 * std::chrono::duration_cast(t_place - t_path).count(); + auto dur_draw = 0.001 * std::chrono::duration_cast(t_draw - t_place).count(); + auto dur_total = 0.001 * std::chrono::duration_cast(t_draw - t_start).count(); + BOOST_LOG_TRIVIAL(info) << + "Total time used creating Tree support for the currently grouped meshes: " << dur_total << " ms. " + "Different subtasks:\nCalculating Avoidance: " << dur_pre_gen << " ms " + "Creating inital influence areas: " << dur_gen << " ms " + "Influence area creation: " << dur_path << "ms " + "Placement of Points in InfluenceAreas: " << dur_place << "ms " + "Drawing result as support " << dur_draw << " ms"; + // if (config.branch_radius==2121) + // BOOST_LOG_TRIVIAL(error) << "Why ask questions when you already know the answer twice.\n (This is not a real bug, please dont report it.)"; + + move_bounds.clear(); + } else { + top_contacts.assign(config.raft_layers.size(), nullptr); + if (generate_raft_contact(print_object, config, top_contacts, layer_storage) < 0) + // No raft. + continue; } -#endif // TREESUPPORT_DEBUG_SVG - - // ### Propagate the influence areas downwards. This is an inherently serial operation. - create_layer_pathing(volumes, config, move_bounds, throw_on_cancel); - auto t_path = std::chrono::high_resolution_clock::now(); - - // ### Set a point in each influence area - create_nodes_from_area(volumes, config, move_bounds, throw_on_cancel); - auto t_place = std::chrono::high_resolution_clock::now(); - - // ### draw these points as circles - - if (print_object.config().support_material_style == smsTree) - draw_areas(*print.get_object(processing.second.front()), volumes, config, overhangs, move_bounds, - bottom_contacts, top_contacts, intermediate_layers, layer_storage, throw_on_cancel); - else { - assert(print_object.config().support_material_style == smsOrganic); - indexed_triangle_set branches = draw_branches(*print.get_object(processing.second.front()), volumes, config, move_bounds, throw_on_cancel); - // Reduce memory footprint. After this point only slice_branches() will use volumes and from that only collisions with zero radius will be used. - volumes.clear_all_but_object_collision(); - slice_branches(*print.get_object(processing.second.front()), volumes, config, overhangs, move_bounds, branches, - bottom_contacts, top_contacts, intermediate_layers, layer_storage, throw_on_cancel); - } - - auto t_draw = std::chrono::high_resolution_clock::now(); - auto dur_pre_gen = 0.001 * std::chrono::duration_cast(t_precalc - t_start).count(); - auto dur_gen = 0.001 * std::chrono::duration_cast(t_gen - t_precalc).count(); - auto dur_path = 0.001 * std::chrono::duration_cast(t_path - t_gen).count(); - auto dur_place = 0.001 * std::chrono::duration_cast(t_place - t_path).count(); - auto dur_draw = 0.001 * std::chrono::duration_cast(t_draw - t_place).count(); - auto dur_total = 0.001 * std::chrono::duration_cast(t_draw - t_start).count(); - BOOST_LOG_TRIVIAL(info) << - "Total time used creating Tree support for the currently grouped meshes: " << dur_total << " ms. " - "Different subtasks:\nCalculating Avoidance: " << dur_pre_gen << " ms " - "Creating inital influence areas: " << dur_gen << " ms " - "Influence area creation: " << dur_path << "ms " - "Placement of Points in InfluenceAreas: " << dur_place << "ms " - "Drawing result as support " << dur_draw << " ms"; -// if (config.branch_radius==2121) -// BOOST_LOG_TRIVIAL(error) << "Why ask questions when you already know the answer twice.\n (This is not a real bug, please dont report it.)"; - - move_bounds.clear(); auto remove_undefined_layers = [](SupportGeneratorLayersPtr &layers) { layers.erase(std::remove_if(layers.begin(), layers.end(), [](const SupportGeneratorLayer* ptr) { return ptr == nullptr; }), layers.end()); @@ -4341,7 +4364,9 @@ void fff_tree_support_generate(PrintObject &print_object, std::function break; ++idx; } - FFFTreeSupport::generate_support_areas(*print_object.print(), BuildVolume(Pointfs{ Vec2d{ -300., -300. }, Vec2d{ -300., +300. }, Vec2d{ +300., +300. }, Vec2d{ +300., -300. } }, 0.), { idx }, throw_on_cancel); + FFFTreeSupport::generate_support_areas(*print_object.print(), + BuildVolume(Pointfs{ Vec2d{ -300., -300. }, Vec2d{ -300., +300. }, Vec2d{ +300., +300. }, Vec2d{ +300., -300. } }, 0.), { idx }, + throw_on_cancel); } } // namespace Slic3r diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 13a9e6e07c..ffd5a6bc37 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -175,24 +175,20 @@ void Field::on_back_to_sys_value() wxString Field::get_tooltip_text(const wxString& default_string) { - wxString tooltip_text(""); - wxString tooltip = _(m_opt.tooltip); - edit_tooltip(tooltip); + if (m_opt.tooltip.empty()) + return ""; std::string opt_id = m_opt_id; - auto hash_pos = opt_id.find("#"); + auto hash_pos = opt_id.find('#'); if (hash_pos != std::string::npos) { opt_id.replace(hash_pos, 1,"["); opt_id += "]"; } - if (tooltip.length() > 0) - tooltip_text = tooltip + "\n" + _(L("default value")) + "\t: " + + return from_u8(m_opt.tooltip) + "\n" + _L("default value") + "\t: " + (boost::iends_with(opt_id, "_gcode") ? "\n" : "") + default_string + (boost::iends_with(opt_id, "_gcode") ? "" : "\n") + - _(L("parameter name")) + "\t: " + opt_id; - - return tooltip_text; + _L("parameter name") + "\t: " + opt_id; } bool Field::is_matched(const std::string& string, const std::string& pattern) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 24f692d114..74f7bf50e1 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -2246,7 +2246,7 @@ void GCodeViewer::load_shells(const Print& print) if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptFFF) { // adds wipe tower's volume - const double max_z = print.objects()[0]->model_object()->get_model()->bounding_box().max(2); + const double max_z = print.objects()[0]->model_object()->get_model()->max_z(); const PrintConfig& config = print.config(); const size_t extruders_count = config.nozzle_diameter.size(); if (extruders_count > 1 && config.wipe_tower && !config.complete_objects) { diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 9d54612539..bbe77b590e 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -140,7 +140,7 @@ void GLCanvas3D::LayersEditing::select_object(const Model &model, int object_id) // Maximum height of an object changes when the object gets rotated or scaled. // Changing maximum height of an object will invalidate the layer heigth editing profile. // m_model_object->bounding_box() is cached, therefore it is cheap even if this method is called frequently. - const float new_max_z = (model_object_new == nullptr) ? 0.0f : static_cast(model_object_new->bounding_box().max.z()); + const float new_max_z = (model_object_new == nullptr) ? 0.0f : static_cast(model_object_new->max_z()); if (m_model_object != model_object_new || this->last_object_id != object_id || m_object_max_z != new_max_z || (model_object_new != nullptr && m_model_object->id() != model_object_new->id())) { m_layer_height_profile.clear(); @@ -1977,7 +1977,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re if (extruders_count > 1 && wt && !co) { // Height of a print (Show at least a slab) - const double height = std::max(m_model->bounding_box().max.z(), 10.0); + const double height = std::max(m_model->max_z(), 10.0); const float x = dynamic_cast(m_config->option("wipe_tower_x"))->value; const float y = dynamic_cast(m_config->option("wipe_tower_y"))->value; @@ -3746,12 +3746,22 @@ void GLCanvas3D::do_reset_skew(const std::string& snapshot_type) if (!snapshot_type.empty()) wxGetApp().plater()->take_snapshot(_(snapshot_type)); + // stores current min_z of instances + std::map, double> min_zs; + if (!snapshot_type.empty()) { + for (int i = 0; i < static_cast(m_model->objects.size()); ++i) { + const ModelObject* obj = m_model->objects[i]; + for (int j = 0; j < static_cast(obj->instances.size()); ++j) { + min_zs[{ i, j }] = obj->instance_bounding_box(j).min.z(); + } + } + } + std::set> done; // keeps track of modified instances - const Selection::IndicesList& idxs = m_selection.get_volume_idxs(); + Selection::EMode selection_mode = m_selection.get_mode(); - for (unsigned int id : idxs) { - const GLVolume* v = m_volumes.volumes[id]; + for (const GLVolume* v : m_volumes.volumes) { int object_idx = v->object_idx(); if (object_idx < 0 || (int)m_model->objects.size() <= object_idx) continue; @@ -3761,14 +3771,30 @@ void GLCanvas3D::do_reset_skew(const std::string& snapshot_type) done.insert(std::pair(object_idx, instance_idx)); + // Mirror instances/volumes ModelObject* model_object = m_model->objects[object_idx]; if (model_object != nullptr) { - model_object->instances[instance_idx]->set_transformation(v->get_instance_transformation()); - model_object->volumes[volume_idx]->set_transformation(v->get_volume_transformation()); + if (selection_mode == Selection::Instance) + model_object->instances[instance_idx]->set_transformation(v->get_instance_transformation()); + else if (selection_mode == Selection::Volume) + model_object->volumes[volume_idx]->set_transformation(v->get_volume_transformation()); model_object->invalidate_bounding_box(); } } + // Fixes sinking/flying instances + for (const std::pair& i : done) { + ModelObject* m = m_model->objects[i.first]; + double shift_z = m->get_instance_min_z(i.second); + // leave sinking instances as sinking + if (min_zs.empty() || min_zs.find({ i.first, i.second })->second >= SINKING_Z_THRESHOLD || shift_z > SINKING_Z_THRESHOLD) { + Vec3d shift(0.0, 0.0, -shift_z); + m_selection.translate(i.first, i.second, shift); + m->translate_instance(i.second, shift); + } + wxGetApp().obj_list()->update_info_items(static_cast(i.first)); + } + post_event(SimpleEvent(EVT_GLCANVAS_RESET_SKEW)); m_dirty = true; @@ -4990,8 +5016,10 @@ void GLCanvas3D::_refresh_if_shown_on_screen() // frequently enough, we call render() here directly when we can. render(); assert(m_initialized); - if (requires_reload_scene) - reload_scene(true); + if (requires_reload_scene) { + if (wxGetApp().plater()->is_view3D_shown()) + reload_scene(true); + } } } diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 520f67aed1..0c4953e3a0 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -480,6 +480,8 @@ static const FileWildcards file_wildcards_by_type[FT_SIZE] = { /* FT_TEX */ { "Texture"sv, { ".png"sv, ".svg"sv } }, /* FT_SL1 */ { "Masked SLA files"sv, { ".sl1"sv, ".sl1s"sv, ".pwmx"sv } }, + + /* FT_ZIP */ { "Zip files"sv, { ".zip"sv } }, }; #if ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR @@ -885,13 +887,9 @@ wxGLContext* GUI_App::init_glcontext(wxGLCanvas& canvas) bool GUI_App::init_opengl() { -#ifdef __linux__ bool status = m_opengl_mgr.init_gl(); m_opengl_initialized = true; return status; -#else - return m_opengl_mgr.init_gl(); -#endif } // gets path to PrusaSlicer.ini, returns semver from first line comment @@ -1368,12 +1366,15 @@ bool GUI_App::on_init_inner() // An ugly solution to GH #5537 in which GUI_App::init_opengl (normally called from events wxEVT_PAINT // and wxEVT_SET_FOCUS before GUI_App::post_init is called) wasn't called before GUI_App::post_init and OpenGL wasn't initialized. -#ifdef __linux__ - if (! m_post_initialized && m_opengl_initialized) { + // Since issue #9774 Where same problem occured on MacOS Ventura, we decided to have this check on MacOS as well. + +#if defined(__linux__) || defined(__APPLE__) + if (!m_post_initialized && m_opengl_initialized) { #else - if (! m_post_initialized) { + if (!m_post_initialized) { #endif m_post_initialized = true; + #ifdef WIN32 this->mainframe->register_win32_callbacks(); #endif @@ -1977,6 +1978,17 @@ void GUI_App::import_model(wxWindow *parent, wxArrayString& input_files) const dialog.GetPaths(input_files); } +void GUI_App::import_zip(wxWindow* parent, wxString& input_file) const +{ + wxFileDialog dialog(parent ? parent : GetTopWindow(), + _L("Choose ZIP file:"), + from_u8(app_config->get_last_dir()), "", + file_wildcards(FT_ZIP), wxFD_OPEN | wxFD_FILE_MUST_EXIST); + + if (dialog.ShowModal() == wxID_OK) + input_file = dialog.GetPath(); +} + void GUI_App::load_gcode(wxWindow* parent, wxString& input_file) const { input_file.Clear(); diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 3f8d47fd77..51900a9b61 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -73,6 +73,8 @@ enum FileType FT_SL1, + FT_ZIP, + FT_SIZE, }; @@ -125,9 +127,7 @@ private: bool m_last_app_conf_lower_version{ false }; EAppMode m_app_mode{ EAppMode::Editor }; bool m_is_recreating_gui{ false }; -#ifdef __linux__ bool m_opengl_initialized{ false }; -#endif wxColour m_color_label_modified; wxColour m_color_label_sys; @@ -252,6 +252,7 @@ public: void keyboard_shortcuts(); void load_project(wxWindow *parent, wxString& input_file) const; void import_model(wxWindow *parent, wxArrayString& input_files) const; + void import_zip(wxWindow* parent, wxString& input_file) const; void load_gcode(wxWindow* parent, wxString& input_file) const; static bool catch_error(std::function cb, const std::string& err); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 6d4c916f19..c16d77751b 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -446,7 +446,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : m_reset_rotation_button->Bind(wxEVT_BUTTON, [this](wxCommandEvent& e) { GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); Selection& selection = canvas->get_selection(); - + selection.setup_cache(); #if ENABLE_WORLD_COORDINATE if (selection.is_single_volume_or_modifier()) { GLVolume* vol = const_cast(selection.get_first_volume()); @@ -468,9 +468,11 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : else return; - // Update rotation at the GLVolumes. - selection.synchronize_unselected_instances(Selection::SyncRotationType::GENERAL); + // Synchronize instances/volumes. + + selection.synchronize_unselected_instances(Selection::SyncRotationType::RESET); selection.synchronize_unselected_volumes(); + // Copy rotation values from GLVolumes into Model (ModelInstance / ModelVolume), trigger background processing. canvas->do_rotate(L("Reset Rotation")); @@ -490,6 +492,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : #if ENABLE_WORLD_COORDINATE GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); Selection& selection = canvas->get_selection(); + selection.setup_cache(); if (selection.is_single_volume_or_modifier()) { GLVolume* vol = const_cast(selection.get_first_volume()); Geometry::Transformation trafo = vol->get_volume_transformation(); @@ -506,6 +509,10 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : else return; + // Synchronize instances/volumes. + selection.synchronize_unselected_instances(Selection::SyncRotationType::GENERAL); + selection.synchronize_unselected_volumes(); + canvas->do_scale(L("Reset scale")); UpdateAndShow(true); #else @@ -750,7 +757,7 @@ void ObjectManipulation::update_settings_value(const Selection& selection) m_new_rotation = volume->get_instance_rotation() * (180.0 / M_PI); m_new_size = volume->get_instance_scaling_factor().cwiseProduct(wxGetApp().model().objects[volume->object_idx()]->raw_mesh_bounding_box().size()); #endif // ENABLE_WORLD_COORDINATE - m_new_scale = volume->get_instance_scaling_factor() * 100.0; + m_new_scale = m_new_size.cwiseQuotient(selection.get_full_unscaled_instance_local_bounding_box().size()) * 100.0; } m_new_enabled = true; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index a522f5533e..7ce8376ba6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -996,6 +996,8 @@ void GLGizmoCut3D::update_raycasters_for_picking_transform() const Vec3d& instance_offset = mo->instances[inst_id]->get_offset(); const double sla_shift = double(m_c->selection_info()->get_sla_shift()); + const bool looking_forward = is_looking_forward(); + for (size_t i = 0; i < connectors.size(); ++i) { const CutConnector& connector = connectors[i]; @@ -1004,9 +1006,8 @@ void GLGizmoCut3D::update_raycasters_for_picking_transform() Vec3d pos = connector.pos + instance_offset; if (connector.attribs.type == CutConnectorType::Dowel && connector.attribs.style == CutConnectorStyle::Prism) { - if (is_looking_forward()) - pos -= static_cast(height - 0.05f) * m_clp_normal; - else + height = 0.05f; + if (!looking_forward) pos += 0.05 * m_clp_normal; } pos[Z] += sla_shift; @@ -2065,6 +2066,8 @@ void GLGizmoCut3D::render_connectors() m_has_invalid_connector = false; m_info_stats.invalidate(); + const bool looking_forward = is_looking_forward(); + for (size_t i = 0; i < connectors.size(); ++i) { const CutConnector& connector = connectors[i]; @@ -2093,20 +2096,19 @@ void GLGizmoCut3D::render_connectors() if (connector.attribs.type == CutConnectorType::Dowel && connector.attribs.style == CutConnectorStyle::Prism) { if (m_connectors_editing) { - if (is_looking_forward()) - pos -= static_cast(height-0.05f) * m_clp_normal; - else + height = 0.05f; + if (!looking_forward) pos += 0.05 * m_clp_normal; } else { - if (is_looking_forward()) + if (looking_forward) pos -= static_cast(height) * m_clp_normal; else pos += static_cast(height) * m_clp_normal; height *= 2; } } - else if (!is_looking_forward()) + else if (!looking_forward) pos += 0.05 * m_clp_normal; const Transform3d view_model_matrix = camera.get_view_matrix() * translation_transform(pos) * m_rotation_m * diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index c9b493b98e..6deda777e5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -667,7 +667,7 @@ void GLGizmoEmboss::on_set_state() get_model_volume(m_volume_id, m_parent.get_selection().get_model()->objects) == nullptr ) { // reopen gizmo when new object is created GLGizmoBase::m_state = GLGizmoBase::Off; - if (wxGetApp().get_mode() == comSimple) + if (wxGetApp().get_mode() == comSimple || wxGetApp().obj_list()->has_selected_cut_object()) // It's impossible to add a part in simple mode return; // start creating new object diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index f7951f97ca..345d733afd 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -238,13 +238,7 @@ void GLGizmoRotate::init_data_from_selection(const Selection& selection) selection.get_bounding_box_in_reference_system(ECoordinatesType::Local) : selection.get_bounding_box_in_current_reference_system(); m_bounding_box = box; m_center = box_trafo.translation(); - m_orient_matrix = Geometry::translation_transform(m_center); - if (!wxGetApp().obj_manipul()->is_world_coordinates() || m_force_local_coordinate) { - const GLVolume& v = *selection.get_first_volume(); - m_orient_matrix = m_orient_matrix * v.get_instance_transformation().get_rotation_matrix(); - if (selection.is_single_volume_or_modifier() && wxGetApp().obj_manipul()->is_local_coordinates() || m_force_local_coordinate) - m_orient_matrix = m_orient_matrix * v.get_volume_transformation().get_rotation_matrix(); - } + m_orient_matrix = box_trafo; m_radius = Offset + m_bounding_box.radius(); m_snap_coarse_in_radius = m_radius / 3.0f; diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index b656f056ae..ab82c10336 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1293,6 +1293,10 @@ void MainFrame::init_menubar_as_editor() [this](wxCommandEvent&) { if (m_plater) m_plater->import_sl1_archive(); }, "import_plater", nullptr, [this](){return m_plater != nullptr && m_plater->get_ui_job_worker().is_idle(); }, this); + append_menu_item(import_menu, wxID_ANY, _L("Import ZIP Achive") + dots, _L("Load a zip achive"), + [this](wxCommandEvent&) { if (m_plater) m_plater->import_zip_archive(); }, "import_plater", nullptr, + [this]() {return m_plater != nullptr; }, this); + import_menu->AppendSeparator(); append_menu_item(import_menu, wxID_ANY, _L("Import &Config") + dots + "\tCtrl+L", _L("Load exported configuration file"), [this](wxCommandEvent&) { load_config_file(); }, "import_config", nullptr, diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 688570f904..e567ac7c66 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -118,6 +118,20 @@ OptionsGroup::OptionsGroup( wxWindow* _parent, const wxString& title, { } +Option::Option(const ConfigOptionDef& _opt, t_config_option_key id) : opt(_opt), opt_id(id) +{ + if (!opt.tooltip.empty()) { + wxString tooltip; + if (opt.opt_key.rfind("branching", 0) == 0) + tooltip = _L("Unavailable for this method.") + "\n"; + tooltip += _(opt.tooltip); + + edit_tooltip(tooltip); + + opt.tooltip = into_u8(tooltip); + } +} + void Line::clear() { if (near_label_widget_win) @@ -517,9 +531,8 @@ void OptionsGroup::clear(bool destroy_custom_ctrl) Line OptionsGroup::create_single_option_line(const Option& option, const std::string& path/* = std::string()*/) const { - wxString tooltip = _(option.opt.tooltip); - edit_tooltip(tooltip); - Line retval{ _(option.opt.label), tooltip }; + Line retval{ _(option.opt.label), from_u8(option.opt.tooltip) }; + retval.label_path = path; retval.append_option(option); return retval; diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp index 88d0ff8bf5..f754505dd4 100644 --- a/src/slic3r/GUI/OptionsGroup.hpp +++ b/src/slic3r/GUI/OptionsGroup.hpp @@ -47,8 +47,7 @@ struct Option { return (rhs.opt_id == this->opt_id); } - Option(const ConfigOptionDef& _opt, t_config_option_key id) : - opt(_opt), opt_id(id) {} + Option(const ConfigOptionDef& _opt, t_config_option_key id); }; using t_option = std::unique_ptr