Merge branch 'master' into fs_emboss

# Conflicts:
#	src/slic3r/GUI/GLCanvas3D.cpp
This commit is contained in:
Filip Sykala - NTB T15p 2022-11-21 10:16:25 +01:00
commit 7c1a513e5a
16 changed files with 924 additions and 414 deletions

View File

@ -1,9 +1,13 @@
min_slic3r_version = 2.5.0-alpha0 min_slic3r_version = 2.5.0-alpha0
1.5.4 Added material profiles for Prusament Resin BioBased60.
1.5.3 Added filament profiles for ColorFabb VarioShore TPU, FormFutura PP, NinjaTek NinjaFlex/Cheetah TPU and for multiple Eolas Prints filaments. Updated bridging settings in 50um and 70um profiles.
1.5.2 Added SLA material profiles.
1.5.1 Renamed filament type "NYLON" to "PA". Updated Adura X profile. Updated PETG fan settings for Prusa MINI (removed fan ramp up). 1.5.1 Renamed filament type "NYLON" to "PA". Updated Adura X profile. Updated PETG fan settings for Prusa MINI (removed fan ramp up).
1.5.0 Updated arachne parameters. Added profiles for Jessie filaments. 1.5.0 Updated arachne parameters. Added profiles for Jessie filaments.
1.5.0-alpha1 Added filament profile for Prusament PA11 Carbon Fiber. Added profiles for multiple 3D-Fuel filaments. 1.5.0-alpha1 Added filament profile for Prusament PA11 Carbon Fiber. Added profiles for multiple 3D-Fuel filaments.
1.5.0-alpha0 Added parameters for Arachne perimeter generator. Changed default seam position. Updated output filename format. 1.5.0-alpha0 Added parameters for Arachne perimeter generator. Changed default seam position. Updated output filename format.
min_slic3r_version = 2.4.0-rc min_slic3r_version = 2.4.0-rc
1.4.8 Added filament and SLA material profiles. Updated settings.
1.4.7 Added filament profile for Prusament PA11 Carbon Fiber. Added profiles for multiple 3D-Fuel filaments. 1.4.7 Added filament profile for Prusament PA11 Carbon Fiber. Added profiles for multiple 3D-Fuel filaments.
1.4.6 Added SLA materials. Updated filament profiles. 1.4.6 Added SLA materials. Updated filament profiles.
1.4.5 Added MMU2/S profiles for 0.25mm nozzle. Updated FW version. Enabled g-code thumbnails for MK3 family printers. Updated end g-code. 1.4.5 Added MMU2/S profiles for 0.25mm nozzle. Updated FW version. Enabled g-code thumbnails for MK3 family printers. Updated end g-code.

View File

@ -5,7 +5,7 @@
name = Prusa Research name = Prusa Research
# Configuration version of this file. Config file will only be installed, if the config_version differs. # 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. # This means, the server may force the PrusaSlicer configuration to be downgraded.
config_version = 1.5.1 config_version = 1.5.4
# Where to get the updates from? # Where to get the updates from?
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/ 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% changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1%
@ -441,8 +441,8 @@ inherits = *common*
layer_height = 0.05 layer_height = 0.05
bottom_solid_layers = 10 bottom_solid_layers = 10
bridge_acceleration = 300 bridge_acceleration = 300
bridge_flow_ratio = 1.15 bridge_flow_ratio = 0.6
bridge_speed = 15 bridge_speed = 25
default_acceleration = 1000 default_acceleration = 1000
external_perimeter_speed = 20 external_perimeter_speed = 20
fill_density = 20% fill_density = 20%
@ -463,12 +463,13 @@ perimeters = 3
support_material_speed = 30 support_material_speed = 30
top_solid_infill_speed = 20 top_solid_infill_speed = 20
top_solid_layers = 15 top_solid_layers = 15
thick_bridges = 1
[print:*0.07mm*] [print:*0.07mm*]
inherits = *0.05mm* inherits = *0.05mm*
layer_height = 0.07 layer_height = 0.07
bottom_solid_layers = 8 bottom_solid_layers = 8
bridge_flow_ratio = 1 bridge_flow_ratio = 0.6
fill_density = 15% fill_density = 15%
infill_speed = 40 infill_speed = 40
solid_infill_speed = 40 solid_infill_speed = 40
@ -583,6 +584,8 @@ top_solid_layers = 4
inherits = *0.05mm* inherits = *0.05mm*
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.4 and num_extruders==1 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.4 and num_extruders==1
infill_extrusion_width = 0.5 infill_extrusion_width = 0.5
support_material_contact_distance = 0.1
raft_contact_distance = 0.1
[print:0.10mm DETAIL] [print:0.10mm DETAIL]
inherits = *0.10mm* inherits = *0.10mm*
@ -892,12 +895,16 @@ fill_pattern = gyroid
fill_density = 15% fill_density = 15%
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and ! single_extruder_multi_material compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and ! single_extruder_multi_material
top_infill_extrusion_width = 0.4 top_infill_extrusion_width = 0.4
support_material_contact_distance = 0.1
raft_contact_distance = 0.1
[print:0.07mm ULTRADETAIL @MK3] [print:0.07mm ULTRADETAIL @MK3]
inherits = *0.07mm*; *MK3* inherits = *0.07mm*; *MK3*
fill_pattern = gyroid fill_pattern = gyroid
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and ! single_extruder_multi_material compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and ! single_extruder_multi_material
top_infill_extrusion_width = 0.4 top_infill_extrusion_width = 0.4
support_material_contact_distance = 0.1
raft_contact_distance = 0.1
[print:0.10mm DETAIL @MK3] [print:0.10mm DETAIL @MK3]
inherits = *0.10mm*; *MK3* inherits = *0.10mm*; *MK3*
@ -1269,6 +1276,8 @@ support_material_xy_spacing = 60%
support_material_speed = 30 support_material_speed = 30
support_material_extrusion_width = 0.35 support_material_extrusion_width = 0.35
bridge_acceleration = 300 bridge_acceleration = 300
support_material_contact_distance = 0.1
raft_contact_distance = 0.1
[print:0.07mm ULTRADETAIL @MINI] [print:0.07mm ULTRADETAIL @MINI]
inherits = *0.07mm*; *MINI* inherits = *0.07mm*; *MINI*
@ -1282,6 +1291,8 @@ external_perimeter_extrusion_width = 0.4
support_material_xy_spacing = 60% support_material_xy_spacing = 60%
support_material_extrusion_width = 0.35 support_material_extrusion_width = 0.35
bridge_acceleration = 300 bridge_acceleration = 300
support_material_contact_distance = 0.1
raft_contact_distance = 0.1
[print:0.10mm DETAIL @MINI] [print:0.10mm DETAIL @MINI]
inherits = *0.10mm*; *MINI* inherits = *0.10mm*; *MINI*
@ -1652,7 +1663,7 @@ filament_retract_length = 4
filament_retract_speed = 40 filament_retract_speed = 40
filament_deretract_speed = 15 filament_deretract_speed = 15
filament_retract_lift = 0 filament_retract_lift = 0
filament_retract_before_travel = 7 filament_retract_before_travel = 6
filament_wipe = 0 filament_wipe = 0
bridge_fan_speed = 80 bridge_fan_speed = 80
fan_always_on = 1 fan_always_on = 1
@ -2879,6 +2890,55 @@ min_print_speed = 15
slowdown_below_layer_time = 10 slowdown_below_layer_time = 10
cooling = 1 cooling = 1
[filament:NinjaTek NinjaFlex TPU]
inherits = *FLEX*
filament_vendor = NinjaTek
fan_always_on = 1
filament_max_volumetric_speed = 1.2
extrusion_multiplier = 1.2
first_layer_temperature = 238
first_layer_bed_temperature = 50
temperature = 238
bed_temperature = 50
bridge_fan_speed = 75
max_fan_speed = 60
min_fan_speed = 60
filament_retract_before_travel = 3
filament_cost = 85
filament_density = 1.19
filament_retract_length = 2.5
filament_retract_speed = 60
filament_deretract_speed = 25
filament_retract_lift = 0
filament_wipe = 0
disable_fan_first_layers = 1
full_fan_speed_layer = 3
min_print_speed = 10
slowdown_below_layer_time = 10
cooling = 1
[filament:NinjaTek Cheetah TPU]
inherits = NinjaTek NinjaFlex TPU
filament_retract_length = 1.5
filament_density = 1.22
filament_max_volumetric_speed = 4
extrusion_multiplier = 1.05
filament_retract_speed = 45
filament_deretract_speed = 25
first_layer_temperature = 240
temperature = 240
[filament:NinjaTek Cheetah TPU @MINI]
inherits = NinjaTek NinjaFlex TPU; *FLEXMINI*
filament_density = 1.22
filament_max_volumetric_speed = 3.5
extrusion_multiplier = 1.05
first_layer_temperature = 240
temperature = 240
filament_retract_speed = 50
filament_deretract_speed = 25
filament_retract_length = 4.8
[filament:Filatech FilaFlex40] [filament:Filatech FilaFlex40]
inherits = *FLEX* inherits = *FLEX*
filament_vendor = Filatech filament_vendor = Filatech
@ -3174,7 +3234,8 @@ filament_retract_layer_change = nil
inherits = Ultrafuse ABS inherits = Ultrafuse ABS
filament_density = 1.08 filament_density = 1.08
first_layer_bed_temperature = 105 first_layer_bed_temperature = 105
temperature = 250 first_layer_temperature = 270
temperature = 270
filament_colour = #FFF8D9 filament_colour = #FFF8D9
filament_notes = "Material Description\nABS Fusion+ made with Polyscope XILOY™ 3D is an engineering filament which has been optimized for 3D-printing. This special grade has been developed in collaboration with Polyscope Polymers - renowned for its material solutions in the automotive industry. ABS is a thermoplastic which is used in many applications. Although ABS has been classified as a standard material in 3D-printing it is known to be quite challenging to process. ABS Fusion+ combines the properties of ABS with an improved processability. The filament is based on an ABS grade which can be directly printed on glass without any adhesives or tape and has a higher success rate of prints due to extreme low warping." filament_notes = "Material Description\nABS Fusion+ made with Polyscope XILOY™ 3D is an engineering filament which has been optimized for 3D-printing. This special grade has been developed in collaboration with Polyscope Polymers - renowned for its material solutions in the automotive industry. ABS is a thermoplastic which is used in many applications. Although ABS has been classified as a standard material in 3D-printing it is known to be quite challenging to process. ABS Fusion+ combines the properties of ABS with an improved processability. The filament is based on an ABS grade which can be directly printed on glass without any adhesives or tape and has a higher success rate of prints due to extreme low warping."
filament_retract_before_travel = 2 filament_retract_before_travel = 2
@ -3943,6 +4004,76 @@ filament_density = 1.24
filament_spool_weight = 230 filament_spool_weight = 230
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) 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)
[filament:Eolas Prints PLA]
inherits = *PLA*
filament_vendor = Eolas Prints
filament_cost = 23.50
filament_density = 1.24
filament_spool_weight = 0
filament_colour = #4D9398
filament_max_volumetric_speed = 15
temperature = 208
[filament:Eolas Prints PLA Matte]
inherits = Eolas Prints PLA
filament_cost = 25.50
filament_max_volumetric_speed = 14
temperature = 212
[filament:Eolas Prints INGEO 850]
inherits = Eolas Prints PLA
filament_cost = 25.90
temperature = 210
[filament:Eolas Prints INGEO 870]
inherits = Eolas Prints PLA
filament_cost = 25.90
temperature = 215
first_layer_bed_temperature = 68
first_layer_temperature = 220
bed_temperature = 65
[filament:Eolas Prints PETG]
inherits = *PET*
filament_vendor = Eolas Prints
filament_cost = 29.90
filament_density = 1.27
filament_spool_weight = 0
filament_colour = #4D9398
filament_max_volumetric_speed = 8
temperature = 240
first_layer_bed_temperature = 85
first_layer_temperature = 235
bed_temperature = 90
filament_retract_length = 1.1
filament_retract_lift = 0.22
[filament:Eolas Prints PETG @MINI]
inherits = Eolas Prints PETG; *PETMINI*
[filament:Eolas Prints PETG - UV Resistant]
inherits = Eolas Prints PETG
filament_cost = 35.90
temperature = 237
first_layer_temperature = 232
[filament:Eolas Prints PETG - UV Resistant @MINI]
inherits = Eolas Prints PETG - UV Resistant; *PETMINI*
[filament:Eolas Prints TPU 93A]
inherits = *FLEX*
filament_vendor = Eolas Prints
filament_cost = 34.99
filament_density = 1.21
filament_spool_weight = 1000
filament_colour = #4D9398
filament_max_volumetric_speed = 1.2
temperature = 235
first_layer_bed_temperature = 30
bed_temperature = 30
filament_retract_length = 0
extrusion_multiplier = 1.16
[filament:Fiberlogy Easy PLA] [filament:Fiberlogy Easy PLA]
inherits = *PLA* inherits = *PLA*
renamed_from = Fiberlogy PLA renamed_from = Fiberlogy PLA
@ -4041,22 +4172,19 @@ disable_fan_first_layers = 5
[filament:Fiberlogy PCTG] [filament:Fiberlogy PCTG]
inherits = Fiberlogy CPE HT inherits = Fiberlogy CPE HT
filament_vendor = Fiberlogy
filament_cost = 29.41 filament_cost = 29.41
filament_density = 1.23 filament_density = 1.23
extrusion_multiplier = 0.98 extrusion_multiplier = 0.98
min_fan_speed = 10 min_fan_speed = 10
max_fan_speed = 15 max_fan_speed = 15
bridge_fan_speed = 50
min_print_speed = 15
first_layer_temperature = 265 first_layer_temperature = 265
temperature = 265 temperature = 265
first_layer_bed_temperature = 90 first_layer_bed_temperature = 90
bed_temperature = 90 bed_temperature = 90
filament_type = CPE filament_type = PCTG
fan_below_layer_time = 20
slowdown_below_layer_time = 15 [filament:Fiberlogy PCTG @MINI]
disable_fan_first_layers = 5 inherits = Fiberlogy PCTG; *PETMINI*
[filament:Fiberlogy FiberFlex 40D] [filament:Fiberlogy FiberFlex 40D]
inherits = *FLEX* inherits = *FLEX*
@ -4331,7 +4459,7 @@ filament_loading_speed_start = 19
filament_minimal_purge_on_wipe_tower = 15 filament_minimal_purge_on_wipe_tower = 15
filament_unloading_speed_start = 100 filament_unloading_speed_start = 100
full_fan_speed_layer = 4 full_fan_speed_layer = 4
filament_max_volumetric_speed = 13 filament_max_volumetric_speed = 12
[filament:Generic PLA @MMU2] [filament:Generic PLA @MMU2]
inherits = *PLA MMU2* inherits = *PLA MMU2*
@ -4350,6 +4478,13 @@ filament_cost = 36.29
filament_density = 1.24 filament_density = 1.24
filament_spool_weight = 201 filament_spool_weight = 201
[filament:Jessie PLA @MMU2]
inherits = *PLA MMU2*
filament_vendor = Printed Solid
filament_cost = 21
filament_density = 1.24
filament_max_volumetric_speed = 10
[filament:Prusament PVB @MMU2] [filament:Prusament PVB @MMU2]
inherits = *PLA MMU2* inherits = *PLA MMU2*
filament_vendor = Prusa Polymers filament_vendor = Prusa Polymers
@ -4402,6 +4537,17 @@ full_fan_speed_layer = 6
filament_retract_length = 1.2 filament_retract_length = 1.2
filament_deretract_speed = 20 filament_deretract_speed = 20
[filament:ColorFabb VarioShore TPU]
inherits = Fillamentum Flexfill 98A
filament_vendor = ColorFabb
filament_colour = #BBBBBB
filament_cost = 71.35
filament_density = 1.22
filament_spool_weight = 0
extrusion_multiplier = 0.85
first_layer_temperature = 220
temperature = 220
[filament:Taulman Bridge] [filament:Taulman Bridge]
inherits = *common* inherits = *common*
filament_vendor = Taulman filament_vendor = Taulman
@ -4653,6 +4799,42 @@ min_fan_speed = 100
start_filament_gcode = "M900 K0 ; Filament gcode" start_filament_gcode = "M900 K0 ; Filament gcode"
temperature = 220 temperature = 220
[filament:FormFutura Centaur PP]
inherits = *common*
filament_vendor = FormFutura
filament_cost = 70
filament_density = 0.89
filament_spool_weight = 212
bridge_fan_speed = 100
cooling = 1
disable_fan_first_layers = 2
extrusion_multiplier = 1.05
fan_always_on = 1
fan_below_layer_time = 100
filament_colour = #DEE0E6
filament_max_volumetric_speed = 4
filament_type = PP
first_layer_bed_temperature = 85
bed_temperature = 85
first_layer_temperature = 235
max_fan_speed = 70
min_fan_speed = 70
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.05{else}0.1{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K28{elsif nozzle_diameter[0]==0.8};{else}M900 K48{endif} ; Filament gcode LA 1.0"
temperature = 235
filament_wipe = 0
filament_retract_lift = 0
compatible_printers_condition = nozzle_diameter[0]>=0.35 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:FormFutura Centaur PP @MINI]
inherits = FormFutura Centaur PP
filament_max_volumetric_speed = 3
filament_retract_length = 3.5
filament_retract_speed = 45
filament_deretract_speed = 20
filament_retract_lift = 0
filament_retract_before_travel = 4
compatible_printers_condition = nozzle_diameter[0]>=0.35 and printer_model=="MINI"
## Filaments MMU1 ## Filaments MMU1
[filament:ColorFabb HT @MMU1] [filament:ColorFabb HT @MMU1]
@ -5775,6 +5957,30 @@ material_type = Tough
material_vendor = Prusa Polymers material_vendor = Prusa Polymers
material_colour = #FCB30E material_colour = #FCB30E
[sla_material:Prusament Resin BioBased60 Herbal Green @0.025]
inherits = *common 0.025*
exposure_time = 7
initial_exposure_time = 30
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #3AD200
[sla_material:Prusament Resin BioBased60 Magma Red @0.025]
inherits = *common 0.025*
exposure_time = 7
initial_exposure_time = 35
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #D20202
[sla_material:Prusament Resin BioBased60 Natural Yellow @0.025]
inherits = *common 0.025*
exposure_time = 6
initial_exposure_time = 35
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #ECDE05
## Prusa 0.025 ## Prusa 0.025
[sla_material:Prusa Orange Tough @0.025] [sla_material:Prusa Orange Tough @0.025]
@ -6732,6 +6938,30 @@ material_type = Tough
material_vendor = Prusa Polymers material_vendor = Prusa Polymers
material_colour = #FCB30E material_colour = #FCB30E
[sla_material:Prusament Resin BioBased60 Herbal Green @0.05]
inherits = *common 0.05*
exposure_time = 8
initial_exposure_time = 30
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #3AD200
[sla_material:Prusament Resin BioBased60 Magma Red @0.05]
inherits = *common 0.05*
exposure_time = 8
initial_exposure_time = 35
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #D20202
[sla_material:Prusament Resin BioBased60 Natural Yellow @0.05]
inherits = *common 0.05*
exposure_time = 7
initial_exposure_time = 35
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #ECDE05
## Prusa 0.05 ## Prusa 0.05
[sla_material:Prusa Beige Tough @0.05] [sla_material:Prusa Beige Tough @0.05]
@ -7120,6 +7350,30 @@ material_type = Tough
material_vendor = Prusa Polymers material_vendor = Prusa Polymers
material_colour = #FCB30E material_colour = #FCB30E
[sla_material:Prusament Resin BioBased60 Herbal Green @0.1]
inherits = *common 0.1*
exposure_time = 13
initial_exposure_time = 45
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #3AD200
[sla_material:Prusament Resin BioBased60 Magma Red @0.1]
inherits = *common 0.1*
exposure_time = 13
initial_exposure_time = 45
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #D20202
[sla_material:Prusament Resin BioBased60 Natural Yellow @0.1]
inherits = *common 0.1*
exposure_time = 8
initial_exposure_time = 35
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #ECDE05
## Prusa 0.1 ## Prusa 0.1
[sla_material:Prusa Orange Tough @0.1] [sla_material:Prusa Orange Tough @0.1]
@ -7336,6 +7590,31 @@ material_type = Tough
material_vendor = Prusa Polymers material_vendor = Prusa Polymers
material_colour = #FCB30E material_colour = #FCB30E
[sla_material:Prusament Resin BioBased60 Herbal Green @0.025 SL1S]
inherits = *0.025_sl1s*
exposure_time = 3.5
initial_exposure_time = 30
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #3AD200
[sla_material:Prusament Resin BioBased60 Magma Red @0.025 SL1S]
inherits = *0.025_sl1s*
exposure_time = 3.5
initial_exposure_time = 30
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #D20202
[sla_material:Prusament Resin BioBased60 Natural Yellow @0.025 SL1S]
inherits = *0.025_sl1s*
exposure_time = 2.8
initial_exposure_time = 30
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #ECDE05
material_print_speed = slow
## Made for Prusa 0.025 ## Made for Prusa 0.025
[sla_material:Prusa Orange Tough @0.025 SL1S] [sla_material:Prusa Orange Tough @0.025 SL1S]
@ -7531,6 +7810,15 @@ material_type = Flexible
material_vendor = BASF material_vendor = BASF
material_colour = #595959 material_colour = #595959
[sla_material:BlueCast X-One @0.025 SL1S]
inherits = *0.025_sl1s*
exposure_time = 6
initial_exposure_time = 25
material_type = Casting
material_vendor = BlueCast
material_colour = #C0C0C0
material_print_speed = slow
[sla_material:PrimaCreator Tough Light Grey @0.025 SL1S] [sla_material:PrimaCreator Tough Light Grey @0.025 SL1S]
inherits = *0.025_sl1s* inherits = *0.025_sl1s*
exposure_time = 1.8 exposure_time = 1.8
@ -7754,6 +8042,31 @@ material_type = Tough
material_vendor = Prusa Polymers material_vendor = Prusa Polymers
material_colour = #FCB30E material_colour = #FCB30E
[sla_material:Prusament Resin BioBased60 Herbal Green @0.05 SL1S]
inherits = *0.05_sl1s*
exposure_time = 4
initial_exposure_time = 30
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #3AD200
[sla_material:Prusament Resin BioBased60 Magma Red @0.05 SL1S]
inherits = *0.05_sl1s*
exposure_time = 4
initial_exposure_time = 30
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #D20202
[sla_material:Prusament Resin BioBased60 Natural Yellow @0.05 SL1S]
inherits = *0.05_sl1s*
exposure_time = 3
initial_exposure_time = 30
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #ECDE05
material_print_speed = slow
## Made for Prusa 0.05 ## Made for Prusa 0.05
[sla_material:Prusa Orange Tough @0.05 SL1S] [sla_material:Prusa Orange Tough @0.05 SL1S]
@ -8128,6 +8441,15 @@ material_type = Tough
material_vendor = BlueCast material_vendor = BlueCast
material_colour = #007EFD material_colour = #007EFD
[sla_material:BlueCast X-One @0.05 SL1S]
inherits = *0.05_sl1s*
exposure_time = 7
initial_exposure_time = 25
material_type = Casting
material_vendor = BlueCast
material_colour = #C0C0C0
material_print_speed = slow
[sla_material:NextDent Model 2.0 Grey @0.05 SL1S] [sla_material:NextDent Model 2.0 Grey @0.05 SL1S]
inherits = *0.05_sl1s* inherits = *0.05_sl1s*
exposure_time = 4 exposure_time = 4
@ -8476,6 +8798,31 @@ material_type = Tough
material_vendor = Prusa Polymers material_vendor = Prusa Polymers
material_colour = #FCB30E material_colour = #FCB30E
[sla_material:Prusament Resin BioBased60 Herbal Green @0.1 SL1S]
inherits = *0.1_sl1s*
exposure_time = 5
initial_exposure_time = 30
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #3AD200
[sla_material:Prusament Resin BioBased60 Magma Red @0.1 SL1S]
inherits = *0.1_sl1s*
exposure_time = 5
initial_exposure_time = 30
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #D20202
[sla_material:Prusament Resin BioBased60 Natural Yellow @0.1 SL1S]
inherits = *0.1_sl1s*
exposure_time = 4
initial_exposure_time = 30
material_type = Tough
material_vendor = Prusa Polymers
material_colour = #ECDE05
material_print_speed = slow
## Made for Prusa 0.1 ## Made for Prusa 0.1
[sla_material:Prusa Orange Tough @0.1 SL1S] [sla_material:Prusa Orange Tough @0.1 SL1S]
@ -8671,6 +9018,15 @@ material_type = Flexible
material_vendor = BASF material_vendor = BASF
material_colour = #595959 material_colour = #595959
[sla_material:BlueCast X-One @0.1 SL1S]
inherits = *0.1_sl1s*
exposure_time = 8.5
initial_exposure_time = 25
material_type = Casting
material_vendor = BlueCast
material_colour = #C0C0C0
material_print_speed = slow
[sla_material:PrimaCreator Tough Light Grey @0.1 SL1S] [sla_material:PrimaCreator Tough Light Grey @0.1 SL1S]
inherits = *0.1_sl1s* inherits = *0.1_sl1s*
exposure_time = 3 exposure_time = 3

View File

@ -25,6 +25,7 @@
namespace Slic3r { namespace Slic3r {
// execute fn for each pixel on the line. If fn returns false, terminate the iteration
template<typename PointFn> void dda(coord_t x0, coord_t y0, coord_t x1, coord_t y1, const PointFn &fn) template<typename PointFn> void dda(coord_t x0, coord_t y0, coord_t x1, coord_t y1, const PointFn &fn)
{ {
coord_t dx = abs(x1 - x0); coord_t dx = abs(x1 - x0);
@ -39,7 +40,7 @@ template<typename PointFn> void dda(coord_t x0, coord_t y0, coord_t x1, coord_t
dy *= 2; dy *= 2;
for (; n > 0; --n) { for (; n > 0; --n) {
fn(x, y); if (!fn(x, y)) return;
if (error > 0) { if (error > 0) {
x += x_inc; x += x_inc;
@ -55,11 +56,11 @@ template<typename PointFn> void dda(coord_t x0, coord_t y0, coord_t x1, coord_t
// may call the fn on the same coordiantes multiple times! // may call the fn on the same coordiantes multiple times!
template<typename PointFn> void double_dda_with_offset(coord_t x0, coord_t y0, coord_t x1, coord_t y1, const PointFn &fn) template<typename PointFn> void double_dda_with_offset(coord_t x0, coord_t y0, coord_t x1, coord_t y1, const PointFn &fn)
{ {
Vec2d normal = Point{y1 - y0, x1 - x0}.cast<double>().normalized(); Vec2d normal = Point{y1 - y0, x1 - x0}.cast<double>().normalized();
normal.x() = ceil(normal.x()); normal.x() = ceil(normal.x());
normal.y() = ceil(normal.y()); normal.y() = ceil(normal.y());
Point start_offset = Point(x0,y0) + (normal).cast<coord_t>(); Point start_offset = Point(x0, y0) + (normal).cast<coord_t>();
Point end_offset = Point(x1,y1) + (normal).cast<coord_t>(); Point end_offset = Point(x1, y1) + (normal).cast<coord_t>();
dda(x0, y0, x1, y1, fn); dda(x0, y0, x1, y1, fn);
dda(start_offset.x(), start_offset.y(), end_offset.x(), end_offset.y(), fn); dda(start_offset.x(), start_offset.y(), end_offset.x(), end_offset.y(), fn);
@ -169,7 +170,10 @@ public:
float goal_heuristic(Node n) const { return n.position == target ? -1.f : (target - n.position).template cast<double>().norm(); } float goal_heuristic(Node n) const { return n.position == target ? -1.f : (target - n.position).template cast<double>().norm(); }
size_t unique_id(Node n) const { return (static_cast<size_t>(uint16_t(n.position.x())) << 16) + static_cast<size_t>(uint16_t(n.position.y())); } size_t unique_id(Node n) const
{
return (static_cast<size_t>(uint16_t(n.position.x())) << 16) + static_cast<size_t>(uint16_t(n.position.y()));
}
const std::vector<CellPositionType> all_directions{{1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}, {-1, -1}, {0, -1}, {1, -1}}; const std::vector<CellPositionType> all_directions{{1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}, {-1, -1}, {0, -1}, {1, -1}};
}; };
@ -189,6 +193,7 @@ void JPSPathFinder::add_obstacles(const Lines &obstacles)
obstacle_min.x() = std::min(obstacle_min.x(), x); obstacle_min.x() = std::min(obstacle_min.x(), x);
obstacle_min.y() = std::min(obstacle_min.y(), y); obstacle_min.y() = std::min(obstacle_min.y(), y);
inpassable.insert(Pixel{x, y}); inpassable.insert(Pixel{x, y});
return true;
}; };
for (const Line &l : obstacles) { for (const Line &l : obstacles) {
@ -200,36 +205,13 @@ void JPSPathFinder::add_obstacles(const Lines &obstacles)
void JPSPathFinder::add_obstacles(const Layer *layer, const Point &global_origin) void JPSPathFinder::add_obstacles(const Layer *layer, const Point &global_origin)
{ {
if (layer != nullptr) { this->print_z = layer->print_z; } if (layer == nullptr) return;
auto store_obstacle = [&](coord_t x, coord_t y) { this->print_z = layer->print_z;
obstacle_max.x() = std::max(obstacle_max.x(), x);
obstacle_max.y() = std::max(obstacle_max.y(), y);
obstacle_min.x() = std::min(obstacle_min.x(), x);
obstacle_min.y() = std::min(obstacle_min.y(), y);
inpassable.insert(Pixel{x, y});
};
Lines obstacles; Lines obstacles;
for (size_t step = 0; step < 3; step++) { obstacles.reserve(layer->malformed_lines.size());
if (layer != nullptr) { for (const Line &l : layer->malformed_lines) { obstacles.push_back(Line{l.a + global_origin, l.b + global_origin}); }
obstacles.insert(obstacles.end(), layer->malformed_lines.begin(), layer->malformed_lines.end()); add_obstacles(obstacles);
layer = layer->lower_layer;
} else {
break;
}
}
for (const Line &l : obstacles) {
Pixel start = pixelize(l.a + global_origin);
Pixel end = pixelize(l.b + global_origin);
double_dda_with_offset(start.x(), start.y(), end.x(), end.y(), store_obstacle);
}
#ifdef DEBUG_FILES
::Slic3r::SVG svg(debug_out_path(("obstacles_jps" + std::to_string(print_z) + "_" + std::to_string(rand() % 1000)).c_str()).c_str(),
get_extents(obstacles));
svg.draw(obstacles);
svg.Close();
#endif
} }
Polyline JPSPathFinder::find_path(const Point &p0, const Point &p1) Polyline JPSPathFinder::find_path(const Point &p0, const Point &p1)
@ -238,15 +220,34 @@ Polyline JPSPathFinder::find_path(const Point &p0, const Point &p1)
Pixel end = pixelize(p1); Pixel end = pixelize(p1);
if (inpassable.empty() || (start - end).cast<float>().norm() < 3.0) { return Polyline{p0, p1}; } if (inpassable.empty() || (start - end).cast<float>().norm() < 3.0) { return Polyline{p0, p1}; }
BoundingBox search_box({start,end,obstacle_max,obstacle_min}); if (inpassable.find(start) != inpassable.end()) {
search_box.max += Pixel(1,1); dda(start.x(), start.y(), end.x(), end.y(), [&](coord_t x, coord_t y) {
search_box.min -= Pixel(1,1); if (inpassable.find(Pixel(x, y)) == inpassable.end() || start == end) { // new start not found yet, and xy passable
start = Pixel(x, y);
return false;
}
return true;
});
}
if (inpassable.find(end) != inpassable.end()) {
dda(end.x(), end.y(), start.x(), start.y(), [&](coord_t x, coord_t y) {
if (inpassable.find(Pixel(x, y)) == inpassable.end() || start == end) { // new start not found yet, and xy passable
end = Pixel(x, y);
return false;
}
return true;
});
}
BoundingBox bounding_square(Points{start,end}); BoundingBox search_box({start, end, obstacle_max, obstacle_min});
bounding_square.max += Pixel(5,5); search_box.max += Pixel(1, 1);
bounding_square.min -= Pixel(5,5); search_box.min -= Pixel(1, 1);
coord_t bounding_square_size = 2*std::max(bounding_square.size().x(),bounding_square.size().y());
BoundingBox bounding_square(Points{start, end});
bounding_square.max += Pixel(5, 5);
bounding_square.min -= Pixel(5, 5);
coord_t bounding_square_size = 2 * std::max(bounding_square.size().x(), bounding_square.size().y());
bounding_square.max.x() += (bounding_square_size - bounding_square.size().x()) / 2; bounding_square.max.x() += (bounding_square_size - bounding_square.size().x()) / 2;
bounding_square.min.x() -= (bounding_square_size - bounding_square.size().x()) / 2; bounding_square.min.x() -= (bounding_square_size - bounding_square.size().x()) / 2;
bounding_square.max.y() += (bounding_square_size - bounding_square.size().y()) / 2; bounding_square.max.y() += (bounding_square_size - bounding_square.size().y()) / 2;
@ -283,9 +284,7 @@ Polyline JPSPathFinder::find_path(const Point &p0, const Point &p1)
closest_qnode = astar_cache[closest_qnode].parent; closest_qnode = astar_cache[closest_qnode].parent;
} }
} else { } else {
for (const auto& node : out_nodes) { for (const auto &node : out_nodes) { out_path.push_back(node.position); }
out_path.push_back(node.position);
}
out_path.push_back(start); out_path.push_back(start);
} }
@ -335,7 +334,11 @@ Polyline JPSPathFinder::find_path(const Point &p0, const Point &p1)
if (i - index_of_last_stored_point < 2) continue; if (i - index_of_last_stored_point < 2) continue;
bool passable = true; bool passable = true;
auto store_obstacle = [&](coord_t x, coord_t y) { auto store_obstacle = [&](coord_t x, coord_t y) {
if (Pixel(x, y) != start && Pixel(x, y) != end && inpassable.find(Pixel(x, y)) != inpassable.end()) { passable = false; }; if (Pixel(x, y) != start && Pixel(x, y) != end && inpassable.find(Pixel(x, y)) != inpassable.end()) {
passable = false;
return false;
}
return true;
}; };
dda(tmp_path.back().x(), tmp_path.back().y(), out_path[i].x(), out_path[i].y(), store_obstacle); dda(tmp_path.back().x(), tmp_path.back().y(), out_path[i].x(), out_path[i].y(), store_obstacle);
if (!passable) { if (!passable) {

View File

@ -339,32 +339,9 @@ void MeasuringImpl::extract_features()
} }
circles_lengths.clear(); // no longer needed, make it obvious circles_lengths.clear(); // no longer needed, make it obvious
// Some of the "circles" may actually be polygons (5-8 vertices). We want them
// detected as edges, but also to remember the center and save it into those edges.
// We will add all such edges manually and delete the detected circles, leaving it
// in circles_idxs so they are not picked again.
assert(circles.size() == circles_idxs.size());
for (int i=circles.size()-1; i>=0; --i) {
if (circles_idxs[i].first == 0 && circles_idxs[i].second == border.size()-1) {
int N = circles_idxs[i].second - circles_idxs[i].first;
if (N <= 8) {
if (N >= 5) { // polygon = 5,6,7,8 vertices
const Vec3d center = std::get<0>(circles[i].get_circle());
for (int j=(int)circles_idxs[i].first + 1; j<=(int)circles_idxs[i].second; ++j)
edges.emplace_back(SurfaceFeature(SurfaceFeatureType::Edge,
border[j - 1], border[j], std::make_optional(center)));
} else {
// This will be handled just like a regular edge (squares, triangles).
circles_idxs.erase(circles_idxs.begin() + i);
}
circles.erase(circles.begin() + i);
}
}
}
// Anything under 5 vertices shall not be considered a circle. // Anything under 5 vertices shall not be considered a circle.
assert(circles_idxs.size() == circles.size()); assert(circles_idxs.size() == circles.size());
for (int i=0; i<int(circles_idxs.size()); ++i) { for (int i=int(circles_idxs.size())-1; i>=0; --i) {
const auto& [start, end] = circles_idxs[i]; const auto& [start, end] = circles_idxs[i];
int N = start >= 0 int N = start >= 0
? end - start + (start == 0 && end == border.size()-1 ? 0 : 1) // last point is the same as first ? end - start + (start == 0 && end == border.size()-1 ? 0 : 1) // last point is the same as first
@ -372,7 +349,15 @@ void MeasuringImpl::extract_features()
if (N < 5) { if (N < 5) {
circles.erase(circles.begin() + i); circles.erase(circles.begin() + i);
circles_idxs.erase(circles_idxs.begin() + i); circles_idxs.erase(circles_idxs.begin() + i);
--i; } else if (N <= 8 && start == 0 && end == border.size()-1) {
// This is a regular 5-8 polygon. Add the edges as edges with a special
// point and remove the circle. Leave the indices in circles_idxs, so
// the edges are not picked up again later.
const Vec3d center = std::get<0>(circles[i].get_circle());
for (int j=1; j<=end; ++j)
edges.emplace_back(SurfaceFeature(SurfaceFeatureType::Edge,
border[j - 1], border[j], std::make_optional(center)));
circles.erase(circles.begin() + i);
} }
} }

View File

@ -768,54 +768,47 @@ ExtrusionPaths sort_and_connect_extra_perimeters(const std::vector<ExtrusionPath
} }
#define EXTRA_PERIMETER_OFFSET_PARAMETERS ClipperLib::jtSquare, 0. #define EXTRA_PERIMETER_OFFSET_PARAMETERS ClipperLib::jtSquare, 0.
//#define EXTRA_PERIM_DEBUG_FILES // #define EXTRA_PERIM_DEBUG_FILES
// Function will generate extra perimeters clipped over nonbridgeable areas of the provided surface and returns both the new perimeters and // Function will generate extra perimeters clipped over nonbridgeable areas of the provided surface and returns both the new perimeters and
// Polygons filled by those clipped perimeters // Polygons filled by those clipped perimeters
std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over_overhangs(const Surface &surface, std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over_overhangs(ExPolygons infill_area,
ExPolygons infill_area,
const Polygons &lower_slices_polygons, const Polygons &lower_slices_polygons,
const Flow &overhang_flow, const Flow &overhang_flow,
size_t standard_perimeters_count,
double scaled_resolution, double scaled_resolution,
const PrintObjectConfig &object_config, const PrintObjectConfig &object_config,
const PrintConfig &print_config) const PrintConfig &print_config)
{ {
coord_t anchors_size = scale_(EXTERNAL_INFILL_MARGIN); coord_t anchors_size = scale_(EXTERNAL_INFILL_MARGIN);
ExPolygons local_area = intersection_ex({surface.expolygon}, infill_area); Polygons anchors = intersection(infill_area, lower_slices_polygons);
ExPolygons anchors = intersection_ex(local_area, lower_slices_polygons); Polygons overhangs = diff(infill_area, lower_slices_polygons);
ExPolygons overhangs = diff_ex(local_area, lower_slices_polygons); if (overhangs.empty()) { return {}; }
if (overhangs.empty()) {
return {};
}
ExPolygons inset_anchors; // anchored area inset by the anchor length Polygons inset_anchors; // anchored area inset by the anchor length
{ {
std::vector<double> deltas{anchors_size * 0.15 + 0.5 * overhang_flow.scaled_spacing(), std::vector<double> deltas{anchors_size * 0.15 + 0.5 * overhang_flow.scaled_spacing(),
anchors_size * 0.33 + 0.5 * overhang_flow.scaled_spacing(), anchors_size * 0.33 + 0.5 * overhang_flow.scaled_spacing(),
anchors_size * 0.66 + 0.5 * overhang_flow.scaled_spacing(), anchors_size * 1.00}; anchors_size * 0.66 + 0.5 * overhang_flow.scaled_spacing(), anchors_size * 1.00};
std::vector<ExPolygons> anchor_areas_w_delta_anchor_size{}; std::vector<Polygons> anchor_areas_w_delta_anchor_size{};
for (double delta : deltas) { for (double delta : deltas) {
anchor_areas_w_delta_anchor_size.push_back(diff_ex(anchors, offset_ex(overhangs, delta, EXTRA_PERIMETER_OFFSET_PARAMETERS))); anchor_areas_w_delta_anchor_size.push_back(diff(anchors, expand(overhangs, delta, EXTRA_PERIMETER_OFFSET_PARAMETERS)));
} }
for (size_t i = 0; i < anchor_areas_w_delta_anchor_size.size() - 1; i++) { for (size_t i = 0; i < anchor_areas_w_delta_anchor_size.size() - 1; i++) {
ExPolygons clipped = diff_ex(anchor_areas_w_delta_anchor_size[i], offset_ex(anchor_areas_w_delta_anchor_size[i + 1], Polygons clipped = diff(anchor_areas_w_delta_anchor_size[i], expand(anchor_areas_w_delta_anchor_size[i + 1],
deltas[i + 1], EXTRA_PERIMETER_OFFSET_PARAMETERS)); deltas[i + 1], EXTRA_PERIMETER_OFFSET_PARAMETERS));
anchor_areas_w_delta_anchor_size[i] = intersection_ex(anchor_areas_w_delta_anchor_size[i], anchor_areas_w_delta_anchor_size[i] = intersection(anchor_areas_w_delta_anchor_size[i],
offset_ex(clipped, deltas[i] + deltas[i + 1], expand(clipped, deltas[i+1] + 0.1*overhang_flow.scaled_spacing(),
EXTRA_PERIMETER_OFFSET_PARAMETERS)); EXTRA_PERIMETER_OFFSET_PARAMETERS));
} }
for (size_t i = 0; i < anchor_areas_w_delta_anchor_size.size(); i++) { for (size_t i = 0; i < anchor_areas_w_delta_anchor_size.size(); i++) {
inset_anchors.insert(inset_anchors.end(), anchor_areas_w_delta_anchor_size[i].begin(), inset_anchors = union_(inset_anchors, anchor_areas_w_delta_anchor_size[i]);
anchor_areas_w_delta_anchor_size[i].end());
} }
inset_anchors = union_ex(inset_anchors); inset_anchors = opening(inset_anchors, 0.8 * deltas[0], EXTRA_PERIMETER_OFFSET_PARAMETERS);
inset_anchors = closing_ex(to_polygons(inset_anchors), 1.0 * overhang_flow.scaled_spacing(), 1.0 * overhang_flow.scaled_spacing(), inset_anchors = closing(inset_anchors, 0.8 * deltas[0], EXTRA_PERIMETER_OFFSET_PARAMETERS);
EXTRA_PERIMETER_OFFSET_PARAMETERS);
#ifdef EXTRA_PERIM_DEBUG_FILES #ifdef EXTRA_PERIM_DEBUG_FILES
{ {
@ -832,70 +825,67 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
#endif #endif
} }
ExPolygons inset_overhang_area = diff_ex(local_area, inset_anchors); Polygons inset_overhang_area = diff(infill_area, inset_anchors);
ExPolygons repeated_area = offset_ex(intersection_ex(offset_ex(inset_overhang_area, 1.10 * overhang_flow.scaled_spacing(),
EXTRA_PERIMETER_OFFSET_PARAMETERS),
inset_anchors),
0.1 * overhang_flow.scaled_spacing(), EXTRA_PERIMETER_OFFSET_PARAMETERS);
#ifdef EXTRA_PERIM_DEBUG_FILES #ifdef EXTRA_PERIM_DEBUG_FILES
{ {
BoundingBox bbox = get_extents(inset_overhangs_w_anchors); BoundingBox bbox = get_extents(inset_overhang_area);
bbox.offset(scale_(1.)); bbox.offset(scale_(1.));
::Slic3r::SVG svg(debug_out_path("inset_overhangs_w_anchors").c_str(), bbox); ::Slic3r::SVG svg(debug_out_path("inset_overhang_area").c_str(), bbox);
for (const Line &line : to_lines(inset_overhangs_w_anchors)) svg.draw(line, "purple", scale_(0.25)); for (const Line &line : to_lines(inset_anchors)) svg.draw(line, "purple", scale_(0.25));
for (const Line &line : to_lines(inset_overhang_area)) svg.draw(line, "red", scale_(0.20)); for (const Line &line : to_lines(inset_overhang_area)) svg.draw(line, "red", scale_(0.15));
for (const Line &line : to_lines(repeated_area)) svg.draw(line, "green", scale_(0.15));
for (const Line &line : to_lines(inset_anchors)) svg.draw(line, "yellow", scale_(0.10));
svg.Close(); svg.Close();
} }
#endif #endif
Polygons area_left_unfilled; Polygons inset_overhang_area_left_unfilled;
std::vector<std::vector<ExtrusionPaths>> extra_perims; // overhang region -> shell -> shell parts std::vector<std::vector<ExtrusionPaths>> extra_perims; // overhang region -> shell -> shell parts
for (const ExPolygon &overhang : inset_overhang_area) { for (const ExPolygon &overhang : union_ex(to_expolygons(inset_overhang_area))) {
ExPolygons overhang_to_cover = {overhang}; Polygons overhang_to_cover = to_polygons(overhang);
overhang_to_cover.insert(overhang_to_cover.end(), repeated_area.begin(), repeated_area.end()); Polygons expanded_overhang_to_cover = expand(overhang_to_cover, 1.1 * overhang_flow.scaled_spacing());
overhang_to_cover = union_ex(overhang_to_cover); Polygons shrinked_overhang_to_cover = shrink(overhang_to_cover, 0.1 * overhang_flow.scaled_spacing());
ExPolygons real_overhang = intersection_ex({overhang_to_cover}, overhangs); Polygons real_overhang = intersection(overhang_to_cover, overhangs);
if (real_overhang.empty()) { if (real_overhang.empty()) {
area_left_unfilled = union_(area_left_unfilled, to_polygons(overhang_to_cover)); inset_overhang_area_left_unfilled.insert(inset_overhang_area_left_unfilled.end(), overhang_to_cover.begin(),
overhang_to_cover.end());
continue; continue;
} }
extra_perims.emplace_back(); extra_perims.emplace_back();
std::vector<ExtrusionPaths> &overhang_region = extra_perims.back(); std::vector<ExtrusionPaths> &overhang_region = extra_perims.back();
ExPolygons anchoring = intersection_ex({overhang_to_cover}, inset_anchors); Polygons anchoring = intersection(expanded_overhang_to_cover, inset_anchors);
ExPolygons perimeter_polygon = offset_ex(overhang_to_cover, -overhang_flow.scaled_spacing() * 0.6); Polygons perimeter_polygon = offset(union_(expand(overhang_to_cover, 0.1 * overhang_flow.scaled_spacing()), anchoring),
-overhang_flow.scaled_spacing() * 0.6);
double perimeter_polygon_area = area(perimeter_polygon);
Polygon anchoring_convex_hull = Geometry::convex_hull(anchoring); Polygon anchoring_convex_hull = Geometry::convex_hull(anchoring);
double unbridgeable_area = area(diff_ex(perimeter_polygon, {anchoring_convex_hull})); double unbridgeable_area = area(diff(real_overhang, {anchoring_convex_hull}));
for (const ExPolygon &exp : perimeter_polygon) { // penalize also holes
for (const Polygon &hole : exp.holes) { unbridgeable_area += std::abs(area(hole)); } for (const Polygon &poly : perimeter_polygon) {
if (poly.is_clockwise()) { // hole, penalize bridges.
unbridgeable_area += std::abs(area(poly));
}
} }
auto [dir, unsupp_dist] = detect_bridging_direction(to_polygons(real_overhang), to_polygons(anchors));
auto [dir, unsupp_dist] = detect_bridging_direction(real_overhang, anchors);
#ifdef EXTRA_PERIM_DEBUG_FILES #ifdef EXTRA_PERIM_DEBUG_FILES
{ {
BoundingBox bbox = get_extents(inset_overhang_area); BoundingBox bbox = get_extents(anchoring_convex_hull);
bbox.offset(scale_(1.)); bbox.offset(scale_(1.));
::Slic3r::SVG svg(debug_out_path(("bridge_check").c_str()).c_str(), bbox); ::Slic3r::SVG svg(debug_out_path("bridge_check").c_str(), bbox);
for (const Line &line : to_lines(perimeter_polygon)) svg.draw(line, "purple", scale_(0.25)); for (const Line &line : to_lines(perimeter_polygon)) svg.draw(line, "purple", scale_(0.25));
for (const Line &line : to_lines(real_overhang)) svg.draw(line, "red", scale_(0.20)); for (const Line &line : to_lines(real_overhang)) svg.draw(line, "red", scale_(0.20));
for (const Line &line : to_lines(anchoring_convex_hull)) svg.draw(line, "green", scale_(0.15)); for (const Line &line : to_lines(anchoring_convex_hull)) svg.draw(line, "green", scale_(0.15));
for (const Line &line : to_lines(anchoring)) svg.draw(line, "yellow", scale_(0.10)); for (const Line &line : to_lines(anchoring)) svg.draw(line, "yellow", scale_(0.10));
for (const Line &line : to_lines(diff_ex(perimeter_polygon, {anchoring_convex_hull}))) svg.draw(line, "black", scale_(0.10)); for (const Line &line : to_lines(diff_ex(perimeter_polygon, {anchoring_convex_hull}))) svg.draw(line, "black", scale_(0.10));
svg.Close(); svg.Close();
} }
#endif #endif
if (unbridgeable_area < 0.2 * area(real_overhang) && unsupp_dist < total_length(real_overhang) * 0.125) {
auto total_length_of_real_overhang = total_length(to_polygons(real_overhang)); inset_overhang_area_left_unfilled.insert(inset_overhang_area_left_unfilled.end(),overhang_to_cover.begin(),overhang_to_cover.end());
if (unbridgeable_area < 0.2 * perimeter_polygon_area && unsupp_dist < total_length_of_real_overhang * 0.5) {
area_left_unfilled = union_(area_left_unfilled, to_polygons(overhang_to_cover));
perimeter_polygon.clear(); perimeter_polygon.clear();
} else { } else {
// fill the overhang with perimeters // fill the overhang with perimeters
@ -903,16 +893,15 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
while (continuation_loops > 0) { while (continuation_loops > 0) {
auto prev = perimeter_polygon; auto prev = perimeter_polygon;
// prepare next perimeter lines // prepare next perimeter lines
Polylines perimeter = intersection_pl(to_polylines(perimeter_polygon), inset_overhang_area); Polylines perimeter = intersection_pl(to_polylines(perimeter_polygon), shrinked_overhang_to_cover);
// do not add the perimeter to result yet, first check if perimeter_polygon is not empty after shrinking - this would mean // do not add the perimeter to result yet, first check if perimeter_polygon is not empty after shrinking - this would mean
// that the polygon was possibly too small for full perimeter loop and in that case try gap fill first // that the polygon was possibly too small for full perimeter loop and in that case try gap fill first
perimeter_polygon.insert(perimeter_polygon.end(), anchoring.begin(), anchoring.end()); perimeter_polygon = union_(perimeter_polygon, anchoring);
perimeter_polygon = union_ex(perimeter_polygon); perimeter_polygon = intersection(offset(perimeter_polygon, -overhang_flow.scaled_spacing()), expanded_overhang_to_cover);
perimeter_polygon = intersection_ex({overhang_to_cover}, offset_ex(perimeter_polygon, -overhang_flow.scaled_spacing()));
if (perimeter_polygon.empty()) { // fill possible gaps of single extrusion width if (perimeter_polygon.empty()) { // fill possible gaps of single extrusion width
ExPolygons shrinked = offset_ex(prev, -0.4 * overhang_flow.scaled_spacing()); Polygons shrinked = offset(prev, -0.4 * overhang_flow.scaled_spacing());
if (!shrinked.empty()) { if (!shrinked.empty()) {
overhang_region.emplace_back(); overhang_region.emplace_back();
extrusion_paths_append(overhang_region.back(), perimeter, erOverhangPerimeter, overhang_flow.mm3_per_mm(), extrusion_paths_append(overhang_region.back(), perimeter, erOverhangPerimeter, overhang_flow.mm3_per_mm(),
@ -922,8 +911,10 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
Polylines fills; Polylines fills;
ExPolygons gap = shrinked.empty() ? offset_ex(prev, overhang_flow.scaled_spacing() * 0.5) : ExPolygons gap = shrinked.empty() ? offset_ex(prev, overhang_flow.scaled_spacing() * 0.5) :
offset_ex(prev, -overhang_flow.scaled_spacing() * 0.5); offset_ex(prev, -overhang_flow.scaled_spacing() * 0.5);
//gap = expolygons_simplify(gap, overhang_flow.scaled_spacing());
for (const ExPolygon &ep : gap) { for (const ExPolygon &ep : gap) {
ep.medial_axis(overhang_flow.scaled_spacing() * 2.0, 0.35 * overhang_flow.scaled_width(), &fills); ep.medial_axis(overhang_flow.scaled_spacing() * 2.0, 0.3 * overhang_flow.scaled_width(), &fills);
} }
if (!fills.empty()) { if (!fills.empty()) {
fills = intersection_pl(fills, inset_overhang_area); fills = intersection_pl(fills, inset_overhang_area);
@ -938,10 +929,7 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
overhang_flow.width(), overhang_flow.height()); overhang_flow.width(), overhang_flow.height());
} }
if (intersection(perimeter_polygon, real_overhang).empty()) { if (intersection(perimeter_polygon, real_overhang).empty()) { continuation_loops--; }
continuation_loops--;
}
if (prev == perimeter_polygon) { if (prev == perimeter_polygon) {
#ifdef EXTRA_PERIM_DEBUG_FILES #ifdef EXTRA_PERIM_DEBUG_FILES
@ -957,15 +945,14 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
break; break;
} }
} }
Polylines perimeter = intersection_pl(to_polylines(perimeter_polygon), inset_overhang_area); Polylines perimeter = intersection_pl(to_polylines(perimeter_polygon), shrinked_overhang_to_cover);
overhang_region.emplace_back(); overhang_region.emplace_back();
extrusion_paths_append(overhang_region.back(), perimeter, erOverhangPerimeter, overhang_flow.mm3_per_mm(), extrusion_paths_append(overhang_region.back(), perimeter, erOverhangPerimeter, overhang_flow.mm3_per_mm(),
overhang_flow.width(), overhang_flow.height()); overhang_flow.width(), overhang_flow.height());
perimeter_polygon = offset_ex(perimeter_polygon, 0.5 * overhang_flow.scaled_spacing()); perimeter_polygon = expand(perimeter_polygon, 0.5 * overhang_flow.scaled_spacing());
perimeter_polygon.insert(perimeter_polygon.end(), anchoring.begin(), anchoring.end()); perimeter_polygon = union_(perimeter_polygon, anchoring);
perimeter_polygon = union_ex(perimeter_polygon); inset_overhang_area_left_unfilled.insert(inset_overhang_area_left_unfilled.end(), perimeter_polygon.begin(),perimeter_polygon.end());
area_left_unfilled = union_(area_left_unfilled, to_polygons(perimeter_polygon));
#ifdef EXTRA_PERIM_DEBUG_FILES #ifdef EXTRA_PERIM_DEBUG_FILES
BoundingBox bbox = get_extents(inset_overhang_area); BoundingBox bbox = get_extents(inset_overhang_area);
@ -974,30 +961,32 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
for (const Line &line : to_lines(perimeter_polygon)) svg.draw(line, "blue", scale_(0.05)); for (const Line &line : to_lines(perimeter_polygon)) svg.draw(line, "blue", scale_(0.05));
for (const Line &line : to_lines(anchoring)) svg.draw(line, "green", scale_(0.05)); for (const Line &line : to_lines(anchoring)) svg.draw(line, "green", scale_(0.05));
for (const Line &line : to_lines(overhang_to_cover)) svg.draw(line, "yellow", scale_(0.05)); for (const Line &line : to_lines(overhang_to_cover)) svg.draw(line, "yellow", scale_(0.05));
for (const Line &line : to_lines(area_left_unfilled)) svg.draw(line, "red", scale_(0.05)); for (const Line &line : to_lines(inset_overhang_area_left_unfilled)) svg.draw(line, "red", scale_(0.05));
svg.Close(); svg.Close();
#endif #endif
std::reverse(overhang_region.begin(), overhang_region.end()); //reverse the order, It shall be printed from inside out std::reverse(overhang_region.begin(), overhang_region.end()); // reverse the order, It shall be printed from inside out
} }
} }
std::vector<ExtrusionPaths> result{}; std::vector<ExtrusionPaths> result{};
for (const std::vector<ExtrusionPaths> &paths : extra_perims) { for (const std::vector<ExtrusionPaths> &paths : extra_perims) {
result.push_back(sort_and_connect_extra_perimeters(paths, 1.5 * overhang_flow.scaled_spacing())); result.push_back(sort_and_connect_extra_perimeters(paths, 2.0 * overhang_flow.scaled_spacing()));
} }
#ifdef EXTRA_PERIM_DEBUG_FILES #ifdef EXTRA_PERIM_DEBUG_FILES
BoundingBox bbox = get_extents(inset_overhang_area); BoundingBox bbox = get_extents(inset_overhang_area);
bbox.offset(scale_(2.)); bbox.offset(scale_(2.));
::Slic3r::SVG svg(debug_out_path(("final" + std::to_string(rand())).c_str()).c_str(), bbox); ::Slic3r::SVG svg(debug_out_path(("final" + std::to_string(rand())).c_str()).c_str(), bbox);
for (const Line &line : to_lines(area_left_unfilled)) svg.draw(line, "blue", scale_(0.05)); for (const Line &line : to_lines(inset_overhang_area_left_unfilled)) svg.draw(line, "blue", scale_(0.05));
for (const Line &line : to_lines(inset_overhangs_w_anchors)) svg.draw(line, "green", scale_(0.05)); for (const Line &line : to_lines(inset_overhang_area)) svg.draw(line, "green", scale_(0.05));
for (const Line &line : to_lines(diff(inset_overhangs_w_anchors, area_left_unfilled))) svg.draw(line, "yellow", scale_(0.05)); for (const Line &line : to_lines(diff(inset_overhang_area, inset_overhang_area_left_unfilled))) svg.draw(line, "yellow", scale_(0.05));
svg.Close(); svg.Close();
#endif #endif
return {result, diff(inset_overhang_area, area_left_unfilled)}; inset_overhang_area_left_unfilled = union_(inset_overhang_area_left_unfilled);
return {result, diff(inset_overhang_area, inset_overhang_area_left_unfilled)};
} }
// Thanks, Cura developers, for implementing an algorithm for generating perimeters with variable width (Arachne) that is based on the paper // Thanks, Cura developers, for implementing an algorithm for generating perimeters with variable width (Arachne) that is based on the paper
@ -1215,31 +1204,23 @@ void PerimeterGenerator::process_arachne()
// collapse too narrow infill areas // collapse too narrow infill areas
const auto min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE)); const auto min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE));
// append infill areas to fill_surfaces // append infill areas to fill_surfaces
this->fill_surfaces->append( // append infill areas to fill_surfaces
offset2_ex( ExPolygons infill_areas = offset2_ex(union_ex(pp), float(-min_perimeter_infill_spacing / 2.),
union_ex(pp), float(inset + min_perimeter_infill_spacing / 2.));
float(- min_perimeter_infill_spacing / 2.), this->fill_surfaces->append(infill_areas, stInternal);
float(inset + min_perimeter_infill_spacing / 2.)),
stInternal);
if (this->lower_slices != nullptr && this->config->overhangs && this->config->extra_perimeters_on_overhangs && if (this->lower_slices != nullptr && this->config->overhangs && this->config->extra_perimeters_on_overhangs &&
this->config->perimeters > 0 && this->layer_id > this->object_config->raft_layers) { this->config->perimeters > 0 && this->layer_id > this->object_config->raft_layers) {
// Generate extra perimeters on overhang areas, and cut them to these parts only, to save print time and material // Generate extra perimeters on overhang areas, and cut them to these parts only, to save print time and material
ExPolygons infill_area; auto [extra_perimeters, filled_area] = generate_extra_perimeters_over_overhangs(infill_areas,
for (const auto &internal_surface : this->fill_surfaces->surfaces) { infill_area.push_back(internal_surface.expolygon); } this->lower_slices_polygons(),
auto [extra_perimeters, this->overhang_flow, this->m_scaled_resolution,
filled_area] = generate_extra_perimeters_over_overhangs(surface, infill_area, *this->object_config, *this->print_config);
this->lower_slices_polygons(),
this->overhang_flow, this->config->perimeters,
this->m_scaled_resolution,
*this->object_config, *this->print_config);
if (!extra_perimeters.empty()) { if (!extra_perimeters.empty()) {
ExtrusionEntityCollection *this_islands_perimeters = static_cast<ExtrusionEntityCollection *>(this->loops->entities.back()); ExtrusionEntityCollection *this_islands_perimeters = static_cast<ExtrusionEntityCollection *>(this->loops->entities.back());
ExtrusionEntityCollection new_perimeters{}; ExtrusionEntityCollection new_perimeters{};
new_perimeters.no_sort = this_islands_perimeters->no_sort; new_perimeters.no_sort = this_islands_perimeters->no_sort;
for (const ExtrusionPaths& paths : extra_perimeters) { for (const ExtrusionPaths &paths : extra_perimeters) { new_perimeters.append(paths); }
new_perimeters.append(paths);
}
new_perimeters.append(this_islands_perimeters->entities); new_perimeters.append(this_islands_perimeters->entities);
this_islands_perimeters->swap(new_perimeters); this_islands_perimeters->swap(new_perimeters);
@ -1516,34 +1497,25 @@ void PerimeterGenerator::process_classic()
// collapse too narrow infill areas // collapse too narrow infill areas
coord_t min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE)); coord_t min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE));
// append infill areas to fill_surfaces // append infill areas to fill_surfaces
this->fill_surfaces->append( ExPolygons infill_areas = offset2_ex(union_ex(pp), float(-inset - min_perimeter_infill_spacing / 2.),
offset2_ex( float(min_perimeter_infill_spacing / 2.));
union_ex(pp), this->fill_surfaces->append(infill_areas, stInternal);
float(- inset - min_perimeter_infill_spacing / 2.),
float(min_perimeter_infill_spacing / 2.)),
stInternal);
if (this->lower_slices != nullptr && this->config->overhangs && this->config->extra_perimeters_on_overhangs && if (this->lower_slices != nullptr && this->config->overhangs && this->config->extra_perimeters_on_overhangs &&
this->config->perimeters > 0 && this->layer_id > this->object_config->raft_layers) { this->config->perimeters > 0 && this->layer_id > this->object_config->raft_layers) {
// Generate extra perimeters on overhang areas, and cut them to these parts only, to save print time and material // Generate extra perimeters on overhang areas, and cut them to these parts only, to save print time and material
ExPolygons infill_area; auto [extra_perimeters, filled_area] = generate_extra_perimeters_over_overhangs(infill_areas,
for (const auto &internal_surface : this->fill_surfaces->surfaces) { infill_area.push_back(internal_surface.expolygon); }
auto [extra_perimeters, filled_area] = generate_extra_perimeters_over_overhangs(surface, infill_area,
this->lower_slices_polygons(), this->lower_slices_polygons(),
this->overhang_flow, this->config->perimeters, this->overhang_flow, this->m_scaled_resolution,
this->m_scaled_resolution,
*this->object_config, *this->print_config); *this->object_config, *this->print_config);
if (!extra_perimeters.empty()) { if (!extra_perimeters.empty()) {
ExtrusionEntityCollection *this_islands_perimeters = static_cast<ExtrusionEntityCollection *>(this->loops->entities.back()); ExtrusionEntityCollection *this_islands_perimeters = static_cast<ExtrusionEntityCollection *>(this->loops->entities.back());
ExtrusionEntityCollection new_perimeters{}; ExtrusionEntityCollection new_perimeters{};
new_perimeters.no_sort = this_islands_perimeters->no_sort; new_perimeters.no_sort = this_islands_perimeters->no_sort;
for (const ExtrusionPaths& paths : extra_perimeters) { for (const ExtrusionPaths &paths : extra_perimeters) { new_perimeters.append(paths); }
new_perimeters.append(paths);
}
new_perimeters.append(this_islands_perimeters->entities); new_perimeters.append(this_islands_perimeters->entities);
this_islands_perimeters->swap(new_perimeters); this_islands_perimeters->swap(new_perimeters);
SurfaceCollection orig_surfaces = *this->fill_surfaces; SurfaceCollection orig_surfaces = *this->fill_surfaces;
this->fill_surfaces->clear(); this->fill_surfaces->clear();
for (const auto &surface : orig_surfaces.surfaces) { for (const auto &surface : orig_surfaces.surfaces) {

View File

@ -58,7 +58,6 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
// Cache the plenty of parameters, which influence the G-code generator only, // Cache the plenty of parameters, which influence the G-code generator only,
// or they are only notes not influencing the generated G-code. // or they are only notes not influencing the generated G-code.
static std::unordered_set<std::string> steps_gcode = { static std::unordered_set<std::string> steps_gcode = {
"avoid_curled_filament_during_travels",
"avoid_crossing_perimeters", "avoid_crossing_perimeters",
"avoid_crossing_perimeters_max_detour", "avoid_crossing_perimeters_max_detour",
"bed_shape", "bed_shape",
@ -223,6 +222,8 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
osteps.emplace_back(posInfill); osteps.emplace_back(posInfill);
osteps.emplace_back(posSupportMaterial); osteps.emplace_back(posSupportMaterial);
steps.emplace_back(psSkirtBrim); steps.emplace_back(psSkirtBrim);
} else if (opt_key == "avoid_curled_filament_during_travels") {
osteps.emplace_back(posEstimateCurledExtrusions);
} else { } else {
// for legacy, if we can't handle this option let's invalidate all steps // for legacy, if we can't handle this option let's invalidate all steps
//FIXME invalidate all steps of all objects as well? //FIXME invalidate all steps of all objects as well?

View File

@ -1231,13 +1231,13 @@ struct LayerCurlingEstimator
if (fabs(dist_from_prev_layer) < 2.0f * flow_width) { if (fabs(dist_from_prev_layer) < 2.0f * flow_width) {
const ExtrusionLine &nearest_line = prev_layer_lines.get_line(nearest_line_idx); const ExtrusionLine &nearest_line = prev_layer_lines.get_line(nearest_line_idx);
current_line.malformation += 0.85 * nearest_line.malformation; current_line.malformation += 0.9 * nearest_line.malformation;
} }
if (dist_from_prev_layer > min_malformation_dist && dist_from_prev_layer < max_malformation_dist) { if (dist_from_prev_layer > min_malformation_dist && dist_from_prev_layer < max_malformation_dist) {
float factor = std::abs(dist_from_prev_layer - (max_malformation_dist + min_malformation_dist) * 0.5) / float factor = 0.5f + 0.5f * std::abs(dist_from_prev_layer - (max_malformation_dist + min_malformation_dist) * 0.5) /
(max_malformation_dist - min_malformation_dist); (max_malformation_dist - min_malformation_dist);
malformation_acc.add_distance(current_line.len); malformation_acc.add_distance(current_line.len);
current_line.malformation += l->height * factor * (2.0f + 3.0f * (malformation_acc.max_curvature / PI)); current_line.malformation += l->height * factor * (1.5f + 3.0f * (malformation_acc.max_curvature / PI));
current_line.malformation = std::min(current_line.malformation, float(l->height * params.max_malformation_factor)); current_line.malformation = std::min(current_line.malformation, float(l->height * params.max_malformation_factor));
} else { } else {
malformation_acc.reset(); malformation_acc.reset();

View File

@ -26,7 +26,7 @@ struct Params {
// the algorithm should use the following units for all computations: distance [mm], mass [g], time [s], force [g*mm/s^2] // the algorithm should use the following units for all computations: distance [mm], mass [g], time [s], force [g*mm/s^2]
const float bridge_distance = 12.0f; //mm const float bridge_distance = 12.0f; //mm
const float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / (1 + this factor * (curvature / PI) ) const float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / (1 + this factor * (curvature / PI) )
const std::pair<float,float> malformation_overlap_factor = std::pair<float, float> { 0.45, -0.1 }; const std::pair<float,float> malformation_overlap_factor = std::pair<float, float> { 0.50, -0.1 };
const float max_malformation_factor = 10.0f; const float max_malformation_factor = 10.0f;
const float min_distance_between_support_points = 3.0f; //mm const float min_distance_between_support_points = 3.0f; //mm

View File

@ -5174,6 +5174,8 @@ bool GLCanvas3D::_init_main_toolbar()
item.left.toggable = true; item.left.toggable = true;
item.left.render_callback = [this](float left, float right, float, float) { item.left.render_callback = [this](float left, float right, float, float) {
if (m_canvas != nullptr) { if (m_canvas != nullptr) {
if (!m_canvas->HasFocus())
m_canvas->SetFocus();
if (_render_search_list(0.5f * (left + right))) if (_render_search_list(0.5f * (left + right)))
_deactivate_search_toolbar_item(); _deactivate_search_toolbar_item();
} }

View File

@ -217,9 +217,7 @@ ObjectList::ObjectList(wxWindow* parent) :
Bind(wxEVT_DATAVIEW_ITEM_DROP_POSSIBLE, &ObjectList::OnDropPossible, this); Bind(wxEVT_DATAVIEW_ITEM_DROP_POSSIBLE, &ObjectList::OnDropPossible, this);
Bind(wxEVT_DATAVIEW_ITEM_DROP, &ObjectList::OnDrop, this); Bind(wxEVT_DATAVIEW_ITEM_DROP, &ObjectList::OnDrop, this);
#ifdef __WXMSW__
Bind(wxEVT_DATAVIEW_ITEM_EDITING_STARTED, &ObjectList::OnEditingStarted, this); Bind(wxEVT_DATAVIEW_ITEM_EDITING_STARTED, &ObjectList::OnEditingStarted, this);
#endif /* __WXMSW__ */
Bind(wxEVT_DATAVIEW_ITEM_EDITING_DONE, &ObjectList::OnEditingDone, this); Bind(wxEVT_DATAVIEW_ITEM_EDITING_DONE, &ObjectList::OnEditingDone, this);
Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &ObjectList::ItemValueChanged, this); Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &ObjectList::ItemValueChanged, this);
@ -1897,12 +1895,10 @@ bool ObjectList::del_subobject_item(wxDataViewItem& item)
// If last volume item with warning was deleted, unmark object item // If last volume item with warning was deleted, unmark object item
if (type & itVolume) { if (type & itVolume) {
add_volumes_to_object_in_list(obj_idx);
const std::string& icon_name = get_warning_icon_name(object(obj_idx)->get_object_stl_stats()); const std::string& icon_name = get_warning_icon_name(object(obj_idx)->get_object_stl_stats());
m_objects_model->UpdateWarningIcon(parent, icon_name); m_objects_model->UpdateWarningIcon(parent, icon_name);
} }
else m_objects_model->Delete(item);
m_objects_model->Delete(item);
update_info_items(obj_idx); update_info_items(obj_idx);
@ -3065,7 +3061,7 @@ bool ObjectList::delete_from_model_and_list(const std::vector<ItemForDelete>& it
if (!del_subobject_from_object(item->obj_idx, item->sub_obj_idx, item->type)) if (!del_subobject_from_object(item->obj_idx, item->sub_obj_idx, item->type))
continue; continue;
if (item->type&itVolume) { if (item->type&itVolume) {
add_volumes_to_object_in_list(item->obj_idx); m_objects_model->Delete(m_objects_model->GetItemByVolumeId(item->obj_idx, item->sub_obj_idx));
ModelObject* obj = object(item->obj_idx); ModelObject* obj = object(item->obj_idx);
if (obj->volumes.size() == 1) { if (obj->volumes.size() == 1) {
wxDataViewItem parent = m_objects_model->GetItemById(item->obj_idx); wxDataViewItem parent = m_objects_model->GetItemById(item->obj_idx);
@ -4653,17 +4649,19 @@ void ObjectList::ItemValueChanged(wxDataViewEvent &event)
} }
} }
void ObjectList::OnEditingStarted(wxDataViewEvent &event)
{
m_is_editing_started = true;
#ifdef __WXMSW__ #ifdef __WXMSW__
// Workaround for entering the column editing mode on Windows. Simulate keyboard enter when another column of the active line is selected. // Workaround for entering the column editing mode on Windows. Simulate keyboard enter when another column of the active line is selected.
// Here the last active column is forgotten, so when leaving the editing mode, the next mouse click will not enter the editing mode of the newly selected column. // Here the last active column is forgotten, so when leaving the editing mode, the next mouse click will not enter the editing mode of the newly selected column.
void ObjectList::OnEditingStarted(wxDataViewEvent &event)
{
m_last_selected_column = -1; m_last_selected_column = -1;
}
#endif //__WXMSW__ #endif //__WXMSW__
}
void ObjectList::OnEditingDone(wxDataViewEvent &event) void ObjectList::OnEditingDone(wxDataViewEvent &event)
{ {
m_is_editing_started = false;
if (event.GetColumn() != colName) if (event.GetColumn() != colName)
return; return;

View File

@ -176,6 +176,7 @@ private:
// Workaround for entering the column editing mode on Windows. Simulate keyboard enter when another column of the active line is selected. // Workaround for entering the column editing mode on Windows. Simulate keyboard enter when another column of the active line is selected.
int m_last_selected_column = -1; int m_last_selected_column = -1;
#endif /* __MSW__ */ #endif /* __MSW__ */
bool m_is_editing_started{ false };
#if 0 #if 0
SettingsFactory::Bundle m_freq_settings_fff; SettingsFactory::Bundle m_freq_settings_fff;
@ -406,6 +407,8 @@ public:
void apply_volumes_order(); void apply_volumes_order();
bool has_paint_on_segmentation(); bool has_paint_on_segmentation();
bool is_editing() const { return m_is_editing_started; }
private: private:
#ifdef __WXOSX__ #ifdef __WXOSX__
// void OnChar(wxKeyEvent& event); // void OnChar(wxKeyEvent& event);
@ -419,10 +422,8 @@ private:
bool can_drop(const wxDataViewItem& item) const ; bool can_drop(const wxDataViewItem& item) const ;
void ItemValueChanged(wxDataViewEvent &event); void ItemValueChanged(wxDataViewEvent &event);
#ifdef __WXMSW__
// Workaround for entering the column editing mode on Windows. Simulate keyboard enter when another column of the active line is selected. // Workaround for entering the column editing mode on Windows. Simulate keyboard enter when another column of the active line is selected.
void OnEditingStarted(wxDataViewEvent &event); void OnEditingStarted(wxDataViewEvent &event);
#endif /* __WXMSW__ */
void OnEditingDone(wxDataViewEvent &event); void OnEditingDone(wxDataViewEvent &event);
}; };

View File

@ -75,10 +75,21 @@ static std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType t
std::string ret; std::string ret;
switch (type) { switch (type) {
case Measure::SurfaceFeatureType::Point: { ret = _u8L("Vertex"); break; } case Measure::SurfaceFeatureType::Point: { ret = _u8L("Vertex"); break; }
case Measure::SurfaceFeatureType::Edge: { ret = (hover_id == POINT_ID) ? _u8L("Center of edge") : _u8L("Point on edge"); break; } case Measure::SurfaceFeatureType::Edge: { ret = _u8L("Point on edge"); break; }
case Measure::SurfaceFeatureType::Circle: { ret = (hover_id == POINT_ID) ? _u8L("Center of circle") : _u8L("Point on circle"); break; } case Measure::SurfaceFeatureType::Circle: { ret = _u8L("Point on circle"); break; }
case Measure::SurfaceFeatureType::Plane: { ret = _u8L("Point on plane"); break; } case Measure::SurfaceFeatureType::Plane: { ret = _u8L("Point on plane"); break; }
default: { assert(false); break; } default: { assert(false); break; }
}
return ret;
}
static std::string center_on_feature_type_as_string(Measure::SurfaceFeatureType type)
{
std::string ret;
switch (type) {
case Measure::SurfaceFeatureType::Edge: { ret = _u8L("Center of edge"); break; }
case Measure::SurfaceFeatureType::Circle: { ret = _u8L("Center of circle"); break; }
default: { assert(false); break; }
} }
return ret; return ret;
} }
@ -251,7 +262,21 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event)
m_mouse_left_down = false; m_mouse_left_down = false;
return false; return false;
} }
else if (mouse_event.Dragging()) {
// Enable/Disable panning/rotating the 3D scene
// Ctrl is pressed or the mouse is not hovering a selected volume
bool unlock_dragging = mouse_event.CmdDown() || (m_hover_id == -1 && !m_parent.get_selection().contains_volume(m_parent.get_first_hover_volume_idx()));
// mode is not center selection or mouse is not hovering a center
unlock_dragging &= !mouse_event.ShiftDown() || (m_hover_id != SELECTION_1_ID && m_hover_id != SELECTION_2_ID && m_hover_id != POINT_ID);
return !unlock_dragging;
}
else if (mouse_event.LeftDown()) { else if (mouse_event.LeftDown()) {
// let the event pass through to allow panning/rotating the 3D scene
if ((m_mode != EMode::CenterSelection && mouse_event.CmdDown()) ||
(m_mode == EMode::CenterSelection && m_hover_id != SELECTION_1_ID && m_hover_id != SELECTION_2_ID && m_hover_id != POINT_ID)) {
return false;
}
if (m_hover_id != -1) { if (m_hover_id != -1) {
SelectedFeatures selected_features_old = m_selected_features; SelectedFeatures selected_features_old = m_selected_features;
m_mouse_left_down = true; m_mouse_left_down = true;
@ -263,10 +288,41 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event)
else if (m_hover_id == SELECTION_2_ID && m_selected_features.second.feature.has_value()) else if (m_hover_id == SELECTION_2_ID && m_selected_features.second.feature.has_value())
item = m_selected_features.second; item = m_selected_features.second;
else { else {
item = { switch (m_mode)
(m_mode == EMode::ExtendedSelection) ? point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id) : surface_feature_type_as_string(m_curr_feature->get_type()), {
(m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(*m_curr_point_on_feature_position) : m_curr_feature case EMode::FeatureSelection:
}; {
item = { surface_feature_type_as_string(m_curr_feature->get_type()), m_curr_feature };
break;
}
case EMode::PointSelection:
{
item = { point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id), Measure::SurfaceFeature(*m_curr_point_on_feature_position) };
break;
}
case EMode::CenterSelection:
{
Vec3d position;
switch (m_curr_feature->get_type())
{
case Measure::SurfaceFeatureType::Circle:
{
position = std::get<0>(m_curr_feature->get_circle());
break;
}
case Measure::SurfaceFeatureType::Edge:
{
assert(m_curr_feature->get_extra_point().has_value());
position = *m_curr_feature->get_extra_point();
break;
}
default: { assert(false); break; }
}
item = { center_on_feature_type_as_string(m_curr_feature->get_type()), Measure::SurfaceFeature(position) };
break;
}
}
} }
return item; return item;
}; };
@ -284,8 +340,14 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event)
m_selected_features.second.reset(); m_selected_features.second.reset();
else { else {
m_selected_features.second = item; m_selected_features.second = item;
if (m_mode == EMode::ExtendedSelection) if (m_mode == EMode::PointSelection || m_mode == EMode::CenterSelection)
m_selection_raycasters.push_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, SELECTION_2_ID, *m_sphere.mesh_raycaster)); m_selection_raycasters.push_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, SELECTION_2_ID, *m_sphere.mesh_raycaster));
if (m_mode == EMode::CenterSelection) {
// Fake ctrl up event to exit the center selection mode
gizmo_event(SLAGizmoEventType::CtrlUp, Vec2d::Zero(), true, false, false);
// increase counter to avoid that keeping the ctrl key pressed triggers a ctrl down event
m_ctrl_kar_filter.increase_count();
}
} }
} }
else { else {
@ -296,8 +358,14 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event)
else { else {
const SelectedFeatures::Item item = item_from_feature(); const SelectedFeatures::Item item = item_from_feature();
m_selected_features.first = item; m_selected_features.first = item;
if (m_mode == EMode::ExtendedSelection) if (m_mode == EMode::PointSelection || m_mode == EMode::CenterSelection)
m_selection_raycasters.push_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, SELECTION_1_ID, *m_sphere.mesh_raycaster)); m_selection_raycasters.push_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, SELECTION_1_ID, *m_sphere.mesh_raycaster));
if (m_mode == EMode::CenterSelection) {
// Fake ctrl up event to exit the center selection mode
gizmo_event(SLAGizmoEventType::CtrlUp, Vec2d::Zero(), true, false, false);
// increase counter to avoid that keeping the ctrl key pressed triggers a ctrl down event
m_ctrl_kar_filter.increase_count();
}
} }
if (m_selected_features != selected_features_old && m_selected_features.second.feature.has_value()) { if (m_selected_features != selected_features_old && m_selected_features.second.feature.has_value()) {
@ -330,10 +398,18 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event)
// avoid closing the gizmo if the user clicks outside of any volume // avoid closing the gizmo if the user clicks outside of any volume
return true; return true;
} }
else if (mouse_event.RightDown() && mouse_event.CmdDown()) { else if (mouse_event.RightDown()) {
m_selected_features.reset(); // let the event pass through to allow panning/rotating the 3D scene
m_selection_raycasters.clear(); if ((m_mode != EMode::CenterSelection && mouse_event.CmdDown()) || (m_mode == EMode::CenterSelection && m_hover_id != SELECTION_1_ID && m_hover_id != SELECTION_2_ID)) {
m_parent.request_extra_frame(); std::cout << "RightDown -> false\n";
return false;
}
if (mouse_event.ShiftDown()) {
m_selected_features.reset();
m_selection_raycasters.clear();
m_parent.request_extra_frame();
}
} }
else if (mouse_event.Leaving()) else if (mouse_event.Leaving())
m_mouse_left_down = false; m_mouse_left_down = false;
@ -369,21 +445,50 @@ void GLGizmoMeasure::data_changed()
m_is_editing_distance_first_frame = true; m_is_editing_distance_first_frame = true;
} }
static bool feature_has_center(std::optional<Measure::SurfaceFeature> feature)
{
return feature.has_value() ?
(feature->get_type() == Measure::SurfaceFeatureType::Circle || (feature->get_type() == Measure::SurfaceFeatureType::Edge && feature->get_extra_point().has_value()))
: false;
}
bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down)
{ {
if (action == SLAGizmoEventType::CtrlDown) { auto activate_center_selection = [this, shift_down, control_down](SLAGizmoEventType action) {
bool ret = false;
switch (action)
{
case SLAGizmoEventType::CtrlDown: { ret = shift_down && feature_has_center(m_curr_feature); break; }
case SLAGizmoEventType::ShiftDown: { ret = control_down && feature_has_center(m_curr_feature); break; }
default: { break; }
}
return ret;
};
if (action == SLAGizmoEventType::ShiftDown) {
if (m_shift_kar_filter.is_first()) {
m_mode = activate_center_selection(SLAGizmoEventType::ShiftDown) ? EMode::CenterSelection : EMode::PointSelection;
disable_scene_raycasters();
}
m_shift_kar_filter.increase_count();
}
else if (action == SLAGizmoEventType::ShiftUp) {
m_shift_kar_filter.reset_count();
m_mode = EMode::FeatureSelection;
restore_scene_raycasters_state();
}
else if (action == SLAGizmoEventType::CtrlDown) {
if (m_ctrl_kar_filter.is_first()) { if (m_ctrl_kar_filter.is_first()) {
if (m_curr_feature.has_value()) { if (activate_center_selection(SLAGizmoEventType::CtrlDown)) {
m_mode = EMode::ExtendedSelection; m_mode = EMode::CenterSelection;
disable_scene_raycasters(); disable_scene_raycasters();
} }
} }
m_ctrl_kar_filter.increase_count(); m_ctrl_kar_filter.increase_count();
} }
else if (action == SLAGizmoEventType::CtrlUp) { else if (action == SLAGizmoEventType::CtrlUp) {
m_ctrl_kar_filter.reset_count(); m_ctrl_kar_filter.reset_count();
m_mode = EMode::BasicSelection; m_mode = control_down ? EMode::PointSelection : EMode::FeatureSelection;
restore_scene_raycasters_state(); restore_scene_raycasters_state();
} }
@ -400,6 +505,7 @@ void GLGizmoMeasure::on_set_state()
{ {
if (m_state == Off) { if (m_state == Off) {
m_ctrl_kar_filter.reset_count(); m_ctrl_kar_filter.reset_count();
m_shift_kar_filter.reset_count();
m_curr_feature.reset(); m_curr_feature.reset();
m_curr_point_on_feature_position.reset(); m_curr_point_on_feature_position.reset();
restore_scene_raycasters_state(); restore_scene_raycasters_state();
@ -408,7 +514,7 @@ void GLGizmoMeasure::on_set_state()
m_measuring.reset(); m_measuring.reset();
} }
else { else {
m_mode = EMode::BasicSelection; m_mode = EMode::FeatureSelection;
// store current state of scene raycaster for later use // store current state of scene raycaster for later use
m_scene_raycasters.clear(); m_scene_raycasters.clear();
auto scene_raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); auto scene_raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume);
@ -467,7 +573,7 @@ void GLGizmoMeasure::on_render()
Vec3f normal_on_model; Vec3f normal_on_model;
size_t model_facet_idx; size_t model_facet_idx;
const bool mouse_on_object = m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, m_volume_matrix, camera, position_on_model, normal_on_model, nullptr, &model_facet_idx); const bool mouse_on_object = m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, m_volume_matrix, camera, position_on_model, normal_on_model, nullptr, &model_facet_idx);
const bool is_hovering_on_locked_feature = m_mode == EMode::ExtendedSelection && m_hover_id != -1; const bool is_hovering_on_feature = (m_mode == EMode::PointSelection || m_mode == EMode::CenterSelection) && m_hover_id != -1;
auto update_circle = [this, inv_zoom]() { auto update_circle = [this, inv_zoom]() {
if (m_last_inv_zoom != inv_zoom || m_last_circle != m_curr_feature) { if (m_last_inv_zoom != inv_zoom || m_last_circle != m_curr_feature) {
@ -483,61 +589,75 @@ void GLGizmoMeasure::on_render()
return false; return false;
}; };
if (m_mode == EMode::BasicSelection) { if (m_mode == EMode::FeatureSelection || m_mode == EMode::PointSelection) {
std::optional<Measure::SurfaceFeature> curr_feature = mouse_on_object ? m_measuring->get_feature(model_facet_idx, position_on_model.cast<double>()) : std::nullopt; if ((m_hover_id == SELECTION_1_ID && boost::algorithm::istarts_with(m_selected_features.first.source, _u8L("Center"))) ||
m_curr_point_on_feature_position.reset(); (m_hover_id == SELECTION_2_ID && boost::algorithm::istarts_with(m_selected_features.second.source, _u8L("Center")))) {
if (m_curr_feature != curr_feature || // Skip feature detection if hovering on a selected center
(curr_feature.has_value() && curr_feature->get_type() == Measure::SurfaceFeatureType::Circle && (m_curr_feature != curr_feature || m_last_inv_zoom != inv_zoom))) { m_curr_feature.reset();
m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID); m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID);
m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID); m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID);
m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID); m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID);
m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID); m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID);
m_raycasters.clear(); m_curr_point_on_feature_position.reset();
m_curr_feature = curr_feature; }
if (!m_curr_feature.has_value()) else {
return; std::optional<Measure::SurfaceFeature> curr_feature = mouse_on_object ? m_measuring->get_feature(model_facet_idx, position_on_model.cast<double>()) : std::nullopt;
if (m_curr_feature != curr_feature ||
(curr_feature.has_value() && curr_feature->get_type() == Measure::SurfaceFeatureType::Circle && (m_curr_feature != curr_feature || m_last_inv_zoom != inv_zoom))) {
m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID);
m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID);
m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID);
m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID);
m_raycasters.clear();
m_curr_feature = curr_feature;
if (!m_curr_feature.has_value())
return;
switch (m_curr_feature->get_type()) { switch (m_curr_feature->get_type()) {
default: { assert(false); break; } default: { assert(false); break; }
case Measure::SurfaceFeatureType::Point: case Measure::SurfaceFeatureType::Point:
{ {
m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) });
break;
}
case Measure::SurfaceFeatureType::Edge:
{
m_raycasters.insert({ EDGE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID, *m_cylinder.mesh_raycaster) });
if (m_curr_feature->get_extra_point().has_value())
m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) });
break; break;
}
case Measure::SurfaceFeatureType::Circle:
{
update_circle();
m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) });
m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) });
break;
}
case Measure::SurfaceFeatureType::Plane:
{
const auto [idx, normal, point] = m_curr_feature->get_plane();
if (m_last_plane_idx != idx) {
m_last_plane_idx = idx;
const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its;
const std::vector<std::vector<int>> planes_triangles = m_measuring->get_planes_triangle_indices();
GLModel::Geometry init_data = init_plane_data(its, planes_triangles, idx);
m_plane.reset();
m_plane.mesh_raycaster = std::make_unique<MeshRaycaster>(std::make_shared<const TriangleMesh>(init_data.get_as_indexed_triangle_set()));
m_plane.model.init_from(std::move(init_data));
} }
case Measure::SurfaceFeatureType::Edge:
{
m_raycasters.insert({ EDGE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID, *m_cylinder.mesh_raycaster) });
if (m_curr_feature->get_extra_point().has_value())
m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) });
break;
}
case Measure::SurfaceFeatureType::Circle:
{
update_circle();
m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) });
m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) });
break;
}
case Measure::SurfaceFeatureType::Plane:
{
const auto [idx, normal, point] = m_curr_feature->get_plane();
if (m_last_plane_idx != idx) {
m_last_plane_idx = idx;
const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its;
const std::vector<std::vector<int>> planes_triangles = m_measuring->get_planes_triangle_indices();
GLModel::Geometry init_data = init_plane_data(its, planes_triangles, idx);
m_plane.reset();
m_plane.mesh_raycaster = std::make_unique<MeshRaycaster>(std::make_shared<const TriangleMesh>(init_data.get_as_indexed_triangle_set()));
m_plane.model.init_from(std::move(init_data));
}
m_raycasters.insert({ PLANE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID, *m_plane.mesh_raycaster) }); m_raycasters.insert({ PLANE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID, *m_plane.mesh_raycaster) });
break; break;
} }
}
} }
} }
} }
else if (is_hovering_on_locked_feature) {
if (m_mode != EMode::PointSelection)
m_curr_point_on_feature_position.reset();
else if (is_hovering_on_feature) {
auto position_on_feature = [this](int feature_type_id, const Camera& camera, std::function<Vec3f(const Vec3f&)> callback = nullptr) -> Vec3d { auto position_on_feature = [this](int feature_type_id, const Camera& camera, std::function<Vec3f(const Vec3f&)> callback = nullptr) -> Vec3d {
auto it = m_raycasters.find(feature_type_id); auto it = m_raycasters.find(feature_type_id);
if (it != m_raycasters.end() && it->second != nullptr) { if (it != m_raycasters.end() && it->second != nullptr) {
@ -554,47 +674,49 @@ void GLGizmoMeasure::on_render()
return Vec3d::Zero(); return Vec3d::Zero();
}; };
switch (m_curr_feature->get_type()) if (m_curr_feature.has_value()) {
{ switch (m_curr_feature->get_type())
default: { assert(false); break; } {
case Measure::SurfaceFeatureType::Point: default: { assert(false); break; }
{ case Measure::SurfaceFeatureType::Point:
m_curr_point_on_feature_position = m_curr_feature->get_point(); {
break; m_curr_point_on_feature_position = m_curr_feature->get_point();
} break;
case Measure::SurfaceFeatureType::Edge: }
{ case Measure::SurfaceFeatureType::Edge:
const std::optional<Vec3d> extra = m_curr_feature->get_extra_point(); {
if (extra.has_value() && m_hover_id == POINT_ID) const std::optional<Vec3d> extra = m_curr_feature->get_extra_point();
m_curr_point_on_feature_position = *extra; if (extra.has_value() && m_hover_id == POINT_ID)
else m_curr_point_on_feature_position = *extra;
m_curr_point_on_feature_position = m_volume_matrix.inverse() * position_on_feature(EDGE_ID, camera, [](const Vec3f& v) { return Vec3f(0.0f, 0.0f, v.z()); }); else
break; m_curr_point_on_feature_position = m_volume_matrix.inverse() * position_on_feature(EDGE_ID, camera, [](const Vec3f& v) { return Vec3f(0.0f, 0.0f, v.z()); });
} break;
case Measure::SurfaceFeatureType::Plane: }
{ case Measure::SurfaceFeatureType::Plane:
m_curr_point_on_feature_position = m_volume_matrix.inverse() * position_on_feature(PLANE_ID, camera); {
break; m_curr_point_on_feature_position = m_volume_matrix.inverse() * position_on_feature(PLANE_ID, camera);
} break;
case Measure::SurfaceFeatureType::Circle: }
{ case Measure::SurfaceFeatureType::Circle:
const auto [center, radius, normal] = m_curr_feature->get_circle(); {
if (m_hover_id == POINT_ID) const auto [center, radius, normal] = m_curr_feature->get_circle();
m_curr_point_on_feature_position = center; if (m_hover_id == POINT_ID)
else { m_curr_point_on_feature_position = center;
const Vec3d world_pof = position_on_feature(CIRCLE_ID, camera, [](const Vec3f& v) { return v; }); else {
const Eigen::Hyperplane<double, 3> plane(m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()* normal, m_volume_matrix * center); const Vec3d world_pof = position_on_feature(CIRCLE_ID, camera, [](const Vec3f& v) { return v; });
const Transform3d local_to_model_matrix = Geometry::translation_transform(center) * Eigen::Quaternion<double>::FromTwoVectors(Vec3d::UnitZ(), normal); const Eigen::Hyperplane<double, 3> plane(m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal, m_volume_matrix * center);
const Vec3d local_proj = local_to_model_matrix.inverse() * m_volume_matrix.inverse() * plane.projection(world_pof); const Transform3d local_to_model_matrix = Geometry::translation_transform(center) * Eigen::Quaternion<double>::FromTwoVectors(Vec3d::UnitZ(), normal);
double angle = std::atan2(local_proj.y(), local_proj.x()); const Vec3d local_proj = local_to_model_matrix.inverse() * m_volume_matrix.inverse() * plane.projection(world_pof);
if (angle < 0.0) double angle = std::atan2(local_proj.y(), local_proj.x());
angle += 2.0 * double(M_PI); if (angle < 0.0)
angle += 2.0 * double(M_PI);
const Vec3d local_pos = radius * Vec3d(std::cos(angle), std::sin(angle), 0.0);
m_curr_point_on_feature_position = local_to_model_matrix * local_pos; const Vec3d local_pos = radius * Vec3d(std::cos(angle), std::sin(angle), 0.0);
m_curr_point_on_feature_position = local_to_model_matrix * local_pos;
}
break;
}
} }
break;
}
} }
} }
else { else {
@ -617,7 +739,6 @@ void GLGizmoMeasure::on_render()
return; return;
shader->start_using(); shader->start_using();
shader->set_uniform("emission_factor", 0.25f);
shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("projection_matrix", camera.get_projection_matrix());
glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
@ -634,8 +755,13 @@ void GLGizmoMeasure::on_render()
shader->set_uniform("view_normal_matrix", view_normal_matrix); shader->set_uniform("view_normal_matrix", view_normal_matrix);
}; };
auto render_feature = [this, set_matrix_uniforms](const Measure::SurfaceFeature& feature, const std::vector<ColorRGBA>& colors, auto set_emission_uniform = [this, shader](const ColorRGBA& color, bool hover) {
float inv_zoom, bool update_raycasters_transform) { shader->set_uniform("emission_factor", (color == m_parent.get_selection().get_first_volume()->render_color) ? 0.0f :
hover ? 0.5f : 0.25f);
};
auto render_feature = [this, set_matrix_uniforms, set_emission_uniform](const Measure::SurfaceFeature& feature, const std::vector<ColorRGBA>& colors,
float inv_zoom, bool hover, bool update_raycasters_transform) {
switch (feature.get_type()) switch (feature.get_type())
{ {
default: { assert(false); break; } default: { assert(false); break; }
@ -644,6 +770,7 @@ void GLGizmoMeasure::on_render()
const Vec3d position = TransformHelper::model_to_world(feature.get_point(), m_volume_matrix); const Vec3d position = TransformHelper::model_to_world(feature.get_point(), m_volume_matrix);
const Transform3d feature_matrix = Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom); const Transform3d feature_matrix = Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom);
set_matrix_uniforms(feature_matrix); set_matrix_uniforms(feature_matrix);
set_emission_uniform(colors.front(), hover);
m_sphere.model.set_color(colors.front()); m_sphere.model.set_color(colors.front());
m_sphere.model.render(); m_sphere.model.render();
if (update_raycasters_transform) { if (update_raycasters_transform) {
@ -657,32 +784,37 @@ void GLGizmoMeasure::on_render()
{ {
const auto& [center, radius, normal] = feature.get_circle(); const auto& [center, radius, normal] = feature.get_circle();
// render center // render center
const Vec3d center_world = TransformHelper::model_to_world(center, m_volume_matrix);
const Transform3d center_matrix = Geometry::translation_transform(center_world) * Geometry::scale_transform(inv_zoom);
set_matrix_uniforms(center_matrix);
m_sphere.model.set_color(colors.front());
m_sphere.model.render();
if (update_raycasters_transform) { if (update_raycasters_transform) {
const Vec3d center_world = TransformHelper::model_to_world(center, m_volume_matrix);
const Transform3d center_matrix = Geometry::translation_transform(center_world) * Geometry::scale_transform(inv_zoom);
set_matrix_uniforms(center_matrix);
set_emission_uniform(colors.front(), hover);
m_sphere.model.set_color(colors.front());
m_sphere.model.render();
auto it = m_raycasters.find(POINT_ID); auto it = m_raycasters.find(POINT_ID);
if (it != m_raycasters.end() && it->second != nullptr) if (it != m_raycasters.end() && it->second != nullptr)
it->second->set_transform(center_matrix); it->second->set_transform(center_matrix);
} }
// render circle // render circle
const Transform3d circle_matrix = Transform3d::Identity(); if (m_mode != EMode::CenterSelection) {
set_matrix_uniforms(circle_matrix); const Transform3d circle_matrix = Transform3d::Identity();
if (update_raycasters_transform) { set_matrix_uniforms(circle_matrix);
m_circle.model.set_color(colors.back()); if (update_raycasters_transform) {
m_circle.model.render(); set_emission_uniform(colors.back(), hover);
auto it = m_raycasters.find(CIRCLE_ID); m_circle.model.set_color(colors.back());
if (it != m_raycasters.end() && it->second != nullptr) m_circle.model.render();
it->second->set_transform(circle_matrix); auto it = m_raycasters.find(CIRCLE_ID);
} if (it != m_raycasters.end() && it->second != nullptr)
else { it->second->set_transform(circle_matrix);
GLModel circle; }
GLModel::Geometry circle_geometry = init_torus_data(64, 16, center.cast<float>(), float(radius), 5.0f * inv_zoom, normal.cast<float>(), m_volume_matrix.cast<float>()); else {
circle.init_from(std::move(circle_geometry)); GLModel circle;
circle.set_color(colors.back()); GLModel::Geometry circle_geometry = init_torus_data(64, 16, center.cast<float>(), float(radius), 5.0f * inv_zoom, normal.cast<float>(), m_volume_matrix.cast<float>());
circle.render(); circle.init_from(std::move(circle_geometry));
set_emission_uniform(colors.back(), hover);
circle.set_color(colors.back());
circle.render();
}
} }
break; break;
} }
@ -690,32 +822,36 @@ void GLGizmoMeasure::on_render()
{ {
const auto& [from, to] = feature.get_edge(); const auto& [from, to] = feature.get_edge();
// render extra point // render extra point
const std::optional<Vec3d> extra = feature.get_extra_point(); if (update_raycasters_transform) {
if (extra.has_value()) { const std::optional<Vec3d> extra = feature.get_extra_point();
const Vec3d extra_world = TransformHelper::model_to_world(*extra, m_volume_matrix); if (extra.has_value()) {
const Transform3d point_matrix = Geometry::translation_transform(extra_world) * Geometry::scale_transform(inv_zoom); const Vec3d extra_world = TransformHelper::model_to_world(*extra, m_volume_matrix);
set_matrix_uniforms(point_matrix); const Transform3d point_matrix = Geometry::translation_transform(extra_world) * Geometry::scale_transform(inv_zoom);
m_sphere.model.set_color(colors.front()); set_matrix_uniforms(point_matrix);
m_sphere.model.render(); set_emission_uniform(colors.front(), hover);
if (update_raycasters_transform) { m_sphere.model.set_color(colors.front());
m_sphere.model.render();
auto it = m_raycasters.find(POINT_ID); auto it = m_raycasters.find(POINT_ID);
if (it != m_raycasters.end() && it->second != nullptr) if (it != m_raycasters.end() && it->second != nullptr)
it->second->set_transform(point_matrix); it->second->set_transform(point_matrix);
} }
} }
// render edge // render edge
const Vec3d from_world = TransformHelper::model_to_world(from, m_volume_matrix); if (m_mode != EMode::CenterSelection) {
const Vec3d to_world = TransformHelper::model_to_world(to, m_volume_matrix); const Vec3d from_world = TransformHelper::model_to_world(from, m_volume_matrix);
const Transform3d edge_matrix = Geometry::translation_transform(from_world) * const Vec3d to_world = TransformHelper::model_to_world(to, m_volume_matrix);
Eigen::Quaternion<double>::FromTwoVectors(Vec3d::UnitZ(), to_world - from_world) * const Transform3d edge_matrix = Geometry::translation_transform(from_world) *
Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (to_world - from_world).norm() }); Eigen::Quaternion<double>::FromTwoVectors(Vec3d::UnitZ(), to_world - from_world) *
set_matrix_uniforms(edge_matrix); Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (to_world - from_world).norm() });
m_cylinder.model.set_color(colors.back()); set_matrix_uniforms(edge_matrix);
m_cylinder.model.render(); set_emission_uniform(colors.back(), hover);
if (update_raycasters_transform) { m_cylinder.model.set_color(colors.back());
auto it = m_raycasters.find(EDGE_ID); m_cylinder.model.render();
if (it != m_raycasters.end() && it->second != nullptr) if (update_raycasters_transform) {
it->second->set_transform(edge_matrix); auto it = m_raycasters.find(EDGE_ID);
if (it != m_raycasters.end() && it->second != nullptr)
it->second->set_transform(edge_matrix);
}
} }
break; break;
} }
@ -724,6 +860,7 @@ void GLGizmoMeasure::on_render()
const auto& [idx, normal, pt] = feature.get_plane(); const auto& [idx, normal, pt] = feature.get_plane();
assert(idx < m_plane_models_cache.size()); assert(idx < m_plane_models_cache.size());
set_matrix_uniforms(m_volume_matrix); set_matrix_uniforms(m_volume_matrix);
set_emission_uniform(colors.front(), hover);
m_plane_models_cache[idx].set_color(colors.front()); m_plane_models_cache[idx].set_color(colors.front());
m_plane_models_cache[idx].render(); m_plane_models_cache[idx].render();
if (update_raycasters_transform) { if (update_raycasters_transform) {
@ -737,19 +874,19 @@ void GLGizmoMeasure::on_render()
}; };
auto hover_selection_color = [this]() { auto hover_selection_color = [this]() {
return saturate(!m_selected_features.first.feature.has_value() ? SELECTED_1ST_COLOR : SELECTED_2ND_COLOR, 1.5f); return !m_selected_features.first.feature.has_value() ? SELECTED_1ST_COLOR : SELECTED_2ND_COLOR;
}; };
auto hovering_color = [this, hover_selection_color, &selection]() { auto hovering_color = [this, hover_selection_color, &selection]() {
return (m_mode == EMode::ExtendedSelection) ? selection.get_first_volume()->render_color : hover_selection_color(); return (m_mode == EMode::PointSelection) ? selection.get_first_volume()->render_color : hover_selection_color();
}; };
if (m_curr_feature.has_value()) { if (m_curr_feature.has_value()) {
std::vector<ColorRGBA> colors; std::vector<ColorRGBA> colors;
if (m_selected_features.first.feature.has_value() && *m_curr_feature == *m_selected_features.first.feature) if (m_selected_features.first.feature.has_value() && *m_curr_feature == *m_selected_features.first.feature)
colors.emplace_back(SELECTED_1ST_COLOR); colors.emplace_back(hovering_color());
else if (m_selected_features.second.feature.has_value() && *m_curr_feature == *m_selected_features.second.feature) else if (m_selected_features.second.feature.has_value() && *m_curr_feature == *m_selected_features.second.feature)
colors.emplace_back(SELECTED_2ND_COLOR); colors.emplace_back(hovering_color());
else { else {
switch (m_curr_feature->get_type()) switch (m_curr_feature->get_type())
{ {
@ -774,12 +911,12 @@ void GLGizmoMeasure::on_render()
} }
} }
render_feature(*m_curr_feature, colors, inv_zoom, true); render_feature(*m_curr_feature, colors, inv_zoom, true, true);
} }
if (m_selected_features.first.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.first.feature)) { if (m_selected_features.first.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.first.feature)) {
const std::vector<ColorRGBA> colors = { SELECTED_1ST_COLOR }; const std::vector<ColorRGBA> colors = { SELECTED_1ST_COLOR };
render_feature(*m_selected_features.first.feature, colors, inv_zoom, false); render_feature(*m_selected_features.first.feature, colors, inv_zoom, m_hover_id == SELECTION_1_ID, false);
if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point) { if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point) {
auto it = std::find_if(m_selection_raycasters.begin(), m_selection_raycasters.end(), auto it = std::find_if(m_selection_raycasters.begin(), m_selection_raycasters.end(),
[](std::shared_ptr<SceneRaycasterItem> item) { return SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, item->get_id()) == SELECTION_1_ID; }); [](std::shared_ptr<SceneRaycasterItem> item) { return SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, item->get_id()) == SELECTION_1_ID; });
@ -789,7 +926,7 @@ void GLGizmoMeasure::on_render()
} }
if (m_selected_features.second.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.second.feature)) { if (m_selected_features.second.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.second.feature)) {
const std::vector<ColorRGBA> colors = { SELECTED_2ND_COLOR }; const std::vector<ColorRGBA> colors = { SELECTED_2ND_COLOR };
render_feature(*m_selected_features.second.feature, colors, inv_zoom, false); render_feature(*m_selected_features.second.feature, colors, inv_zoom, m_hover_id == SELECTION_2_ID, false);
if (m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { if (m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) {
auto it = std::find_if(m_selection_raycasters.begin(), m_selection_raycasters.end(), auto it = std::find_if(m_selection_raycasters.begin(), m_selection_raycasters.end(),
[](std::shared_ptr<SceneRaycasterItem> item) { return SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, item->get_id()) == SELECTION_2_ID; }); [](std::shared_ptr<SceneRaycasterItem> item) { return SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, item->get_id()) == SELECTION_2_ID; });
@ -798,12 +935,14 @@ void GLGizmoMeasure::on_render()
} }
} }
if (is_hovering_on_locked_feature && m_curr_point_on_feature_position.has_value()) { if (is_hovering_on_feature && m_curr_point_on_feature_position.has_value()) {
if (m_hover_id != POINT_ID) { if (m_hover_id != POINT_ID) {
const Vec3d position = TransformHelper::model_to_world(*m_curr_point_on_feature_position, m_volume_matrix); const Vec3d position = TransformHelper::model_to_world(*m_curr_point_on_feature_position, m_volume_matrix);
const Transform3d matrix = Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom); const Transform3d matrix = Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom);
set_matrix_uniforms(matrix); set_matrix_uniforms(matrix);
m_sphere.model.set_color(hover_selection_color()); const ColorRGBA color = hover_selection_color();
set_emission_uniform(color, true);
m_sphere.model.set_color(color);
m_sphere.model.render(); m_sphere.model.render();
} }
} }
@ -1343,12 +1482,15 @@ void GLGizmoMeasure::render_debug_dialog()
{ {
case Measure::SurfaceFeatureType::Point: case Measure::SurfaceFeatureType::Point:
{ {
add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(item.feature->get_point()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); const Vec3d position = m_volume_matrix * item.feature->get_point();
add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text));
break; break;
} }
case Measure::SurfaceFeatureType::Edge: case Measure::SurfaceFeatureType::Edge:
{ {
auto [from, to] = item.feature->get_edge(); auto [from, to] = item.feature->get_edge();
from = m_volume_matrix * from;
to = m_volume_matrix * to;
add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text));
add_strings_row_to_table(*m_imgui, "m_pt2", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_strings_row_to_table(*m_imgui, "m_pt2", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text));
break; break;
@ -1356,6 +1498,8 @@ void GLGizmoMeasure::render_debug_dialog()
case Measure::SurfaceFeatureType::Plane: case Measure::SurfaceFeatureType::Plane:
{ {
auto [idx, normal, origin] = item.feature->get_plane(); auto [idx, normal, origin] = item.feature->get_plane();
origin = m_volume_matrix * origin;
normal = m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal;
add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text));
add_strings_row_to_table(*m_imgui, "m_pt2", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_strings_row_to_table(*m_imgui, "m_pt2", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text));
add_strings_row_to_table(*m_imgui, "m_value", ImGuiWrapper::COL_ORANGE_LIGHT, format_double(idx), ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_strings_row_to_table(*m_imgui, "m_value", ImGuiWrapper::COL_ORANGE_LIGHT, format_double(idx), ImGui::GetStyleColorVec4(ImGuiCol_Text));
@ -1364,6 +1508,10 @@ void GLGizmoMeasure::render_debug_dialog()
case Measure::SurfaceFeatureType::Circle: case Measure::SurfaceFeatureType::Circle:
{ {
auto [center, radius, normal] = item.feature->get_circle(); auto [center, radius, normal] = item.feature->get_circle();
const Vec3d on_circle = m_volume_matrix * (center + radius * Measure::get_orthogonal(normal, true));
center = m_volume_matrix * center;
normal = (m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal).normalized();
radius = (on_circle - center).norm();
add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text));
add_strings_row_to_table(*m_imgui, "m_pt2", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_strings_row_to_table(*m_imgui, "m_pt2", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text));
add_strings_row_to_table(*m_imgui, "m_value", ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_strings_row_to_table(*m_imgui, "m_value", ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text));
@ -1376,6 +1524,28 @@ void GLGizmoMeasure::render_debug_dialog()
}; };
m_imgui->begin(_L("Measure tool debug"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); m_imgui->begin(_L("Measure tool debug"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
if (ImGui::BeginTable("Mode", 2)) {
std::string txt;
switch (m_mode)
{
case EMode::FeatureSelection: { txt = "Feature selection"; break; }
case EMode::PointSelection: { txt = "Point selection"; break; }
case EMode::CenterSelection: { txt = "Center selection"; break; }
default: { assert(false); break; }
}
add_strings_row_to_table(*m_imgui, "Mode", ImGuiWrapper::COL_ORANGE_LIGHT, txt, ImGui::GetStyleColorVec4(ImGuiCol_Text));
ImGui::EndTable();
}
ImGui::Separator();
if (ImGui::BeginTable("Hover", 2)) {
add_strings_row_to_table(*m_imgui, "Hover id", ImGuiWrapper::COL_ORANGE_LIGHT, std::to_string(m_hover_id), ImGui::GetStyleColorVec4(ImGuiCol_Text));
const std::string txt = m_curr_feature.has_value() ? surface_feature_type_as_string(m_curr_feature->get_type()) : "None";
add_strings_row_to_table(*m_imgui, "Current feature", ImGuiWrapper::COL_ORANGE_LIGHT, txt, ImGui::GetStyleColorVec4(ImGuiCol_Text));
ImGui::EndTable();
}
ImGui::Separator();
if (!m_selected_features.first.feature.has_value() && !m_selected_features.second.feature.has_value()) if (!m_selected_features.first.feature.has_value() && !m_selected_features.second.feature.has_value())
m_imgui->text("Empty selection"); m_imgui->text("Empty selection");
else { else {
@ -1402,7 +1572,7 @@ void GLGizmoMeasure::render_debug_dialog()
void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit) void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit)
{ {
static std::optional<Measure::SurfaceFeature> last_feature; static std::optional<Measure::SurfaceFeature> last_feature;
static EMode last_mode = EMode::BasicSelection; static EMode last_mode = EMode::FeatureSelection;
static SelectedFeatures last_selected_features; static SelectedFeatures last_selected_features;
static float last_y = 0.0f; static float last_y = 0.0f;
@ -1436,12 +1606,13 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit
std::string text; std::string text;
ColorRGBA color; ColorRGBA color;
if (m_selected_features.second.feature.has_value()) { if (m_selected_features.second.feature.has_value()) {
if (m_selected_features.second.feature == m_curr_feature && m_mode == EMode::BasicSelection) if (m_selected_features.second.feature == m_curr_feature && m_mode == EMode::FeatureSelection)
text = _u8L("Unselect feature"); text = _u8L("Unselect feature");
else if (m_hover_id == SELECTION_2_ID) else if (m_hover_id == SELECTION_2_ID)
text = _u8L("Unselect point"); text = (m_mode == EMode::CenterSelection) ? _u8L("Unselect center") : _u8L("Unselect point");
else else
text = (m_mode == EMode::BasicSelection) ? _u8L("Select feature") : _u8L("Select point"); text = (m_mode == EMode::PointSelection) ? _u8L("Select point") :
(m_mode == EMode::CenterSelection) ? _u8L("Select center") : _u8L("Select feature");
color = SELECTED_2ND_COLOR; color = SELECTED_2ND_COLOR;
} }
else { else {
@ -1449,11 +1620,12 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit
if (m_selected_features.first.feature == m_curr_feature) if (m_selected_features.first.feature == m_curr_feature)
text = _u8L("Unselect feature"); text = _u8L("Unselect feature");
else if (m_hover_id == SELECTION_1_ID) else if (m_hover_id == SELECTION_1_ID)
text = _u8L("Unselect point"); text = (m_mode == EMode::CenterSelection) ? _u8L("Unselect center") : _u8L("Unselect point");
color = SELECTED_1ST_COLOR; color = SELECTED_1ST_COLOR;
} }
if (text.empty()) { if (text.empty()) {
text = (m_mode == EMode::BasicSelection) ? _u8L("Select feature") : _u8L("Select point"); text = (m_mode == EMode::PointSelection) ? _u8L("Select point") :
(m_mode == EMode::CenterSelection) ? _u8L("Select center") : _u8L("Select feature");
color = m_selected_features.first.feature.has_value() ? SELECTED_2ND_COLOR : SELECTED_1ST_COLOR; color = m_selected_features.first.feature.has_value() ? SELECTED_2ND_COLOR : SELECTED_1ST_COLOR;
} }
} }
@ -1467,18 +1639,23 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit
} }
); );
if (m_selected_features.first.feature.has_value()) { if (m_mode == EMode::FeatureSelection && m_hover_id != -1) {
add_strings_row_to_table(*m_imgui, CTRL_STR + "+" + _u8L("Right mouse button"), ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Restart selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_strings_row_to_table(*m_imgui, _u8L("Shift"), ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text));
++row_count; ++row_count;
} }
if (m_mode == EMode::BasicSelection && m_hover_id != -1) { if (m_mode != EMode::CenterSelection && feature_has_center(m_curr_feature)) {
add_strings_row_to_table(*m_imgui, CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_strings_row_to_table(*m_imgui, _u8L("Shift") + "+" + CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable center selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text));
++row_count;
}
if (m_selected_features.first.feature.has_value()) {
add_strings_row_to_table(*m_imgui, _u8L("Shift") + "+" + _u8L("Right mouse button"), ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Restart selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text));
++row_count; ++row_count;
} }
// add dummy rows to keep dialog size fixed // add dummy rows to keep dialog size fixed
for (unsigned int i = row_count; i < 3; ++i) { for (unsigned int i = row_count; i < 4; ++i) {
add_strings_row_to_table(*m_imgui, " ", ImGuiWrapper::COL_ORANGE_LIGHT, " ", ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_strings_row_to_table(*m_imgui, " ", ImGuiWrapper::COL_ORANGE_LIGHT, " ", ImGui::GetStyleColorVec4(ImGuiCol_Text));
} }
@ -1492,16 +1669,22 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit
//bool data_text_set = false; //bool data_text_set = false;
//ImGui::Separator(); //ImGui::Separator();
//if (feature_type != Measure::SurfaceFeatureType::Undef) { //if (feature_type != Measure::SurfaceFeatureType::Undef) {
// if (m_mode == EMode::BasicSelection) { // if (m_mode == EMode::FeatureSelection) {
// m_imgui->text(surface_feature_type_as_string(feature_type)); // m_imgui->text(surface_feature_type_as_string(feature_type));
// data_text_set = true; // data_text_set = true;
// } // }
// else if (m_mode == EMode::ExtendedSelection) { // else if (m_mode == EMode::PointSelection) {
// if (m_hover_id != -1 && m_curr_point_on_feature_position.has_value()) { // if (m_hover_id != -1 && m_curr_point_on_feature_position.has_value()) {
// m_imgui->text(point_on_feature_type_as_string(feature_type, m_hover_id)); // m_imgui->text(point_on_feature_type_as_string(feature_type, m_hover_id));
// data_text_set = true; // data_text_set = true;
// } // }
// } // }
// else if (m_mode == EMode::CenterSelection) {
// if (m_hover_id != -1 && m_curr_point_on_feature_position.has_value()) {
// m_imgui->text(center_on_feature_type_as_string(feature_type));
// data_text_set = true;
// }
// }
//} //}
//if (!data_text_set) //if (!data_text_set)
// m_imgui->text(_u8L("No feature")); // m_imgui->text(_u8L("No feature"));
@ -1509,7 +1692,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit
//const unsigned int max_data_row_count = 3; //const unsigned int max_data_row_count = 3;
//unsigned int data_row_count = 0; //unsigned int data_row_count = 0;
//if (ImGui::BeginTable("Data", 2)) { //if (ImGui::BeginTable("Data", 2)) {
// if (m_mode == EMode::BasicSelection) { // if (m_mode == EMode::FeatureSelection) {
// switch (feature_type) // switch (feature_type)
// { // {
// default: { break; } // default: { break; }

View File

@ -23,8 +23,9 @@ class GLGizmoMeasure : public GLGizmoBase
{ {
enum class EMode : unsigned char enum class EMode : unsigned char
{ {
BasicSelection, FeatureSelection,
ExtendedSelection PointSelection,
CenterSelection
}; };
struct SelectedFeatures struct SelectedFeatures
@ -67,7 +68,7 @@ class GLGizmoMeasure : public GLGizmoBase
} }
}; };
EMode m_mode{ EMode::BasicSelection }; EMode m_mode{ EMode::FeatureSelection };
Measure::MeasurementResult m_measurement_result; Measure::MeasurementResult m_measurement_result;
std::unique_ptr<Measure::Measuring> m_measuring; // PIMPL std::unique_ptr<Measure::Measuring> m_measuring; // PIMPL
@ -114,6 +115,7 @@ class GLGizmoMeasure : public GLGizmoBase
Vec2d m_mouse_pos{ Vec2d::Zero() }; Vec2d m_mouse_pos{ Vec2d::Zero() };
KeyAutoRepeatFilter m_ctrl_kar_filter; KeyAutoRepeatFilter m_ctrl_kar_filter;
KeyAutoRepeatFilter m_shift_kar_filter;
SelectedFeatures m_selected_features; SelectedFeatures m_selected_features;
bool m_pending_scale{ false }; bool m_pending_scale{ false };

View File

@ -26,6 +26,7 @@ enum class SLAGizmoEventType : unsigned char {
SelectAll, SelectAll,
CtrlDown, CtrlDown,
CtrlUp, CtrlUp,
ShiftDown,
ShiftUp, ShiftUp,
AltUp, AltUp,
ApplyChanges, ApplyChanges,

View File

@ -608,10 +608,8 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
const int keyCode = evt.GetKeyCode(); const int keyCode = evt.GetKeyCode();
bool processed = false; bool processed = false;
if (evt.GetEventType() == wxEVT_KEY_UP) if (evt.GetEventType() == wxEVT_KEY_UP) {
{ if (m_current == SlaSupports || m_current == Hollow || m_current == Cut) {
if (m_current == SlaSupports || m_current == Hollow || m_current == Cut)
{
GLGizmoBase* gizmo = get_current(); GLGizmoBase* gizmo = get_current();
const bool is_editing = m_current == Hollow ? true : gizmo->is_in_editing_mode(); const bool is_editing = m_current == Hollow ? true : gizmo->is_in_editing_mode();
const bool is_rectangle_dragging = gizmo->is_selection_rectangle_dragging(); const bool is_rectangle_dragging = gizmo->is_selection_rectangle_dragging();
@ -627,18 +625,19 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
processed = true; processed = true;
} }
} }
else if (m_current == Measure && keyCode == WXK_CONTROL) { else if (m_current == Measure) {
gizmo_event(SLAGizmoEventType::CtrlUp, Vec2d::Zero(), false); if (keyCode == WXK_CONTROL)
gizmo_event(SLAGizmoEventType::CtrlUp, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown());
else if (keyCode == WXK_SHIFT)
gizmo_event(SLAGizmoEventType::ShiftUp, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown());
} }
// if (processed) // if (processed)
// m_parent.set_cursor(GLCanvas3D::Standard); // m_parent.set_cursor(GLCanvas3D::Standard);
} }
else if (evt.GetEventType() == wxEVT_KEY_DOWN) else if (evt.GetEventType() == wxEVT_KEY_DOWN) {
{ if (m_current == SlaSupports && (keyCode == WXK_SHIFT || keyCode == WXK_ALT)
if ((m_current == SlaSupports) && ((keyCode == WXK_SHIFT) || (keyCode == WXK_ALT)) && get_current()->is_in_editing_mode()) {
&& get_current()->is_in_editing_mode())
{
// m_parent.set_cursor(GLCanvas3D::Cross); // m_parent.set_cursor(GLCanvas3D::Cross);
processed = true; processed = true;
} }
@ -664,8 +663,11 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
if (simplify != nullptr) if (simplify != nullptr)
processed = simplify->on_esc_key_down(); processed = simplify->on_esc_key_down();
} }
else if (m_current == Measure && keyCode == WXK_CONTROL) { else if (m_current == Measure) {
gizmo_event(SLAGizmoEventType::CtrlDown, Vec2d::Zero(), true); if (keyCode == WXK_CONTROL)
gizmo_event(SLAGizmoEventType::CtrlDown, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown());
else if (keyCode == WXK_SHIFT)
gizmo_event(SLAGizmoEventType::ShiftDown, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown());
} }
} }

View File

@ -4890,12 +4890,12 @@ void Plater::priv::set_bed_shape(const Pointfs& shape, const double max_print_he
bool Plater::priv::can_delete() const bool Plater::priv::can_delete() const
{ {
return !get_selection().is_empty() && !get_selection().is_wipe_tower(); return !get_selection().is_empty() && !get_selection().is_wipe_tower() && !sidebar->obj_list()->is_editing();
} }
bool Plater::priv::can_delete_all() const bool Plater::priv::can_delete_all() const
{ {
return !model.objects.empty(); return !model.objects.empty() && !sidebar->obj_list()->is_editing();
} }
bool Plater::priv::can_fix_through_netfabb() const bool Plater::priv::can_fix_through_netfabb() const