Merge branch 'master' into fs_emboss
# Conflicts: # src/slic3r/GUI/GLCanvas3D.cpp
This commit is contained in:
commit
7c1a513e5a
@ -1,9 +1,13 @@
|
||||
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.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-alpha0 Added parameters for Arachne perimeter generator. Changed default seam position. Updated output filename format.
|
||||
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.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.
|
||||
|
@ -5,7 +5,7 @@
|
||||
name = Prusa Research
|
||||
# Configuration version of this file. Config file will only be installed, if the config_version differs.
|
||||
# This means, the server may force the PrusaSlicer configuration to be downgraded.
|
||||
config_version = 1.5.1
|
||||
config_version = 1.5.4
|
||||
# Where to get the updates from?
|
||||
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/
|
||||
changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1%
|
||||
@ -441,8 +441,8 @@ inherits = *common*
|
||||
layer_height = 0.05
|
||||
bottom_solid_layers = 10
|
||||
bridge_acceleration = 300
|
||||
bridge_flow_ratio = 1.15
|
||||
bridge_speed = 15
|
||||
bridge_flow_ratio = 0.6
|
||||
bridge_speed = 25
|
||||
default_acceleration = 1000
|
||||
external_perimeter_speed = 20
|
||||
fill_density = 20%
|
||||
@ -463,12 +463,13 @@ perimeters = 3
|
||||
support_material_speed = 30
|
||||
top_solid_infill_speed = 20
|
||||
top_solid_layers = 15
|
||||
thick_bridges = 1
|
||||
|
||||
[print:*0.07mm*]
|
||||
inherits = *0.05mm*
|
||||
layer_height = 0.07
|
||||
bottom_solid_layers = 8
|
||||
bridge_flow_ratio = 1
|
||||
bridge_flow_ratio = 0.6
|
||||
fill_density = 15%
|
||||
infill_speed = 40
|
||||
solid_infill_speed = 40
|
||||
@ -583,6 +584,8 @@ top_solid_layers = 4
|
||||
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
|
||||
infill_extrusion_width = 0.5
|
||||
support_material_contact_distance = 0.1
|
||||
raft_contact_distance = 0.1
|
||||
|
||||
[print:0.10mm DETAIL]
|
||||
inherits = *0.10mm*
|
||||
@ -892,12 +895,16 @@ fill_pattern = gyroid
|
||||
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
|
||||
top_infill_extrusion_width = 0.4
|
||||
support_material_contact_distance = 0.1
|
||||
raft_contact_distance = 0.1
|
||||
|
||||
[print:0.07mm ULTRADETAIL @MK3]
|
||||
inherits = *0.07mm*; *MK3*
|
||||
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
|
||||
top_infill_extrusion_width = 0.4
|
||||
support_material_contact_distance = 0.1
|
||||
raft_contact_distance = 0.1
|
||||
|
||||
[print:0.10mm DETAIL @MK3]
|
||||
inherits = *0.10mm*; *MK3*
|
||||
@ -1269,6 +1276,8 @@ support_material_xy_spacing = 60%
|
||||
support_material_speed = 30
|
||||
support_material_extrusion_width = 0.35
|
||||
bridge_acceleration = 300
|
||||
support_material_contact_distance = 0.1
|
||||
raft_contact_distance = 0.1
|
||||
|
||||
[print:0.07mm ULTRADETAIL @MINI]
|
||||
inherits = *0.07mm*; *MINI*
|
||||
@ -1282,6 +1291,8 @@ external_perimeter_extrusion_width = 0.4
|
||||
support_material_xy_spacing = 60%
|
||||
support_material_extrusion_width = 0.35
|
||||
bridge_acceleration = 300
|
||||
support_material_contact_distance = 0.1
|
||||
raft_contact_distance = 0.1
|
||||
|
||||
[print:0.10mm DETAIL @MINI]
|
||||
inherits = *0.10mm*; *MINI*
|
||||
@ -1652,7 +1663,7 @@ filament_retract_length = 4
|
||||
filament_retract_speed = 40
|
||||
filament_deretract_speed = 15
|
||||
filament_retract_lift = 0
|
||||
filament_retract_before_travel = 7
|
||||
filament_retract_before_travel = 6
|
||||
filament_wipe = 0
|
||||
bridge_fan_speed = 80
|
||||
fan_always_on = 1
|
||||
@ -2879,6 +2890,55 @@ min_print_speed = 15
|
||||
slowdown_below_layer_time = 10
|
||||
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]
|
||||
inherits = *FLEX*
|
||||
filament_vendor = Filatech
|
||||
@ -3174,7 +3234,8 @@ filament_retract_layer_change = nil
|
||||
inherits = Ultrafuse ABS
|
||||
filament_density = 1.08
|
||||
first_layer_bed_temperature = 105
|
||||
temperature = 250
|
||||
first_layer_temperature = 270
|
||||
temperature = 270
|
||||
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_retract_before_travel = 2
|
||||
@ -3943,6 +4004,76 @@ filament_density = 1.24
|
||||
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)
|
||||
|
||||
[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]
|
||||
inherits = *PLA*
|
||||
renamed_from = Fiberlogy PLA
|
||||
@ -4041,22 +4172,19 @@ disable_fan_first_layers = 5
|
||||
|
||||
[filament:Fiberlogy PCTG]
|
||||
inherits = Fiberlogy CPE HT
|
||||
filament_vendor = Fiberlogy
|
||||
filament_cost = 29.41
|
||||
filament_density = 1.23
|
||||
extrusion_multiplier = 0.98
|
||||
min_fan_speed = 10
|
||||
max_fan_speed = 15
|
||||
bridge_fan_speed = 50
|
||||
min_print_speed = 15
|
||||
first_layer_temperature = 265
|
||||
temperature = 265
|
||||
first_layer_bed_temperature = 90
|
||||
bed_temperature = 90
|
||||
filament_type = CPE
|
||||
fan_below_layer_time = 20
|
||||
slowdown_below_layer_time = 15
|
||||
disable_fan_first_layers = 5
|
||||
filament_type = PCTG
|
||||
|
||||
[filament:Fiberlogy PCTG @MINI]
|
||||
inherits = Fiberlogy PCTG; *PETMINI*
|
||||
|
||||
[filament:Fiberlogy FiberFlex 40D]
|
||||
inherits = *FLEX*
|
||||
@ -4331,7 +4459,7 @@ filament_loading_speed_start = 19
|
||||
filament_minimal_purge_on_wipe_tower = 15
|
||||
filament_unloading_speed_start = 100
|
||||
full_fan_speed_layer = 4
|
||||
filament_max_volumetric_speed = 13
|
||||
filament_max_volumetric_speed = 12
|
||||
|
||||
[filament:Generic PLA @MMU2]
|
||||
inherits = *PLA MMU2*
|
||||
@ -4350,6 +4478,13 @@ filament_cost = 36.29
|
||||
filament_density = 1.24
|
||||
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]
|
||||
inherits = *PLA MMU2*
|
||||
filament_vendor = Prusa Polymers
|
||||
@ -4402,6 +4537,17 @@ full_fan_speed_layer = 6
|
||||
filament_retract_length = 1.2
|
||||
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]
|
||||
inherits = *common*
|
||||
filament_vendor = Taulman
|
||||
@ -4653,6 +4799,42 @@ min_fan_speed = 100
|
||||
start_filament_gcode = "M900 K0 ; Filament gcode"
|
||||
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
|
||||
|
||||
[filament:ColorFabb HT @MMU1]
|
||||
@ -5775,6 +5957,30 @@ material_type = Tough
|
||||
material_vendor = Prusa Polymers
|
||||
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
|
||||
|
||||
[sla_material:Prusa Orange Tough @0.025]
|
||||
@ -6732,6 +6938,30 @@ material_type = Tough
|
||||
material_vendor = Prusa Polymers
|
||||
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
|
||||
|
||||
[sla_material:Prusa Beige Tough @0.05]
|
||||
@ -7120,6 +7350,30 @@ material_type = Tough
|
||||
material_vendor = Prusa Polymers
|
||||
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
|
||||
|
||||
[sla_material:Prusa Orange Tough @0.1]
|
||||
@ -7336,6 +7590,31 @@ material_type = Tough
|
||||
material_vendor = Prusa Polymers
|
||||
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
|
||||
|
||||
[sla_material:Prusa Orange Tough @0.025 SL1S]
|
||||
@ -7531,6 +7810,15 @@ material_type = Flexible
|
||||
material_vendor = BASF
|
||||
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]
|
||||
inherits = *0.025_sl1s*
|
||||
exposure_time = 1.8
|
||||
@ -7754,6 +8042,31 @@ material_type = Tough
|
||||
material_vendor = Prusa Polymers
|
||||
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
|
||||
|
||||
[sla_material:Prusa Orange Tough @0.05 SL1S]
|
||||
@ -8128,6 +8441,15 @@ material_type = Tough
|
||||
material_vendor = BlueCast
|
||||
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]
|
||||
inherits = *0.05_sl1s*
|
||||
exposure_time = 4
|
||||
@ -8476,6 +8798,31 @@ material_type = Tough
|
||||
material_vendor = Prusa Polymers
|
||||
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
|
||||
|
||||
[sla_material:Prusa Orange Tough @0.1 SL1S]
|
||||
@ -8671,6 +9018,15 @@ material_type = Flexible
|
||||
material_vendor = BASF
|
||||
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]
|
||||
inherits = *0.1_sl1s*
|
||||
exposure_time = 3
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
||||
for (; n > 0; --n) {
|
||||
fn(x, y);
|
||||
if (!fn(x, y)) return;
|
||||
|
||||
if (error > 0) {
|
||||
x += x_inc;
|
||||
@ -58,8 +59,8 @@ template<typename PointFn> void double_dda_with_offset(coord_t x0, coord_t y0, c
|
||||
Vec2d normal = Point{y1 - y0, x1 - x0}.cast<double>().normalized();
|
||||
normal.x() = ceil(normal.x());
|
||||
normal.y() = ceil(normal.y());
|
||||
Point start_offset = Point(x0,y0) + (normal).cast<coord_t>();
|
||||
Point end_offset = Point(x1,y1) + (normal).cast<coord_t>();
|
||||
Point start_offset = Point(x0, y0) + (normal).cast<coord_t>();
|
||||
Point end_offset = Point(x1, y1) + (normal).cast<coord_t>();
|
||||
|
||||
dda(x0, y0, x1, y1, 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(); }
|
||||
|
||||
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}};
|
||||
};
|
||||
@ -189,6 +193,7 @@ void JPSPathFinder::add_obstacles(const Lines &obstacles)
|
||||
obstacle_min.x() = std::min(obstacle_min.x(), x);
|
||||
obstacle_min.y() = std::min(obstacle_min.y(), y);
|
||||
inpassable.insert(Pixel{x, y});
|
||||
return true;
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
if (layer != nullptr) { this->print_z = layer->print_z; }
|
||||
if (layer == nullptr) return;
|
||||
|
||||
auto store_obstacle = [&](coord_t x, coord_t y) {
|
||||
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});
|
||||
};
|
||||
this->print_z = layer->print_z;
|
||||
Lines obstacles;
|
||||
for (size_t step = 0; step < 3; step++) {
|
||||
if (layer != nullptr) {
|
||||
obstacles.insert(obstacles.end(), layer->malformed_lines.begin(), layer->malformed_lines.end());
|
||||
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
|
||||
obstacles.reserve(layer->malformed_lines.size());
|
||||
for (const Line &l : layer->malformed_lines) { obstacles.push_back(Line{l.a + global_origin, l.b + global_origin}); }
|
||||
add_obstacles(obstacles);
|
||||
}
|
||||
|
||||
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);
|
||||
if (inpassable.empty() || (start - end).cast<float>().norm() < 3.0) { return Polyline{p0, p1}; }
|
||||
|
||||
BoundingBox search_box({start,end,obstacle_max,obstacle_min});
|
||||
search_box.max += Pixel(1,1);
|
||||
search_box.min -= Pixel(1,1);
|
||||
if (inpassable.find(start) != inpassable.end()) {
|
||||
dda(start.x(), start.y(), end.x(), end.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
|
||||
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});
|
||||
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());
|
||||
BoundingBox search_box({start, end, obstacle_max, obstacle_min});
|
||||
search_box.max += Pixel(1, 1);
|
||||
search_box.min -= Pixel(1, 1);
|
||||
|
||||
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.min.x() -= (bounding_square_size - bounding_square.size().x()) / 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;
|
||||
}
|
||||
} else {
|
||||
for (const auto& node : out_nodes) {
|
||||
out_path.push_back(node.position);
|
||||
}
|
||||
for (const auto &node : out_nodes) { out_path.push_back(node.position); }
|
||||
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;
|
||||
bool passable = true;
|
||||
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);
|
||||
if (!passable) {
|
||||
|
@ -339,32 +339,9 @@ void MeasuringImpl::extract_features()
|
||||
}
|
||||
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.
|
||||
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];
|
||||
int N = start >= 0
|
||||
? 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) {
|
||||
circles.erase(circles.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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -768,54 +768,47 @@ ExtrusionPaths sort_and_connect_extra_perimeters(const std::vector<ExtrusionPath
|
||||
}
|
||||
|
||||
#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
|
||||
// Polygons filled by those clipped perimeters
|
||||
std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over_overhangs(const Surface &surface,
|
||||
ExPolygons infill_area,
|
||||
std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over_overhangs(ExPolygons infill_area,
|
||||
const Polygons &lower_slices_polygons,
|
||||
const Flow &overhang_flow,
|
||||
size_t standard_perimeters_count,
|
||||
double scaled_resolution,
|
||||
const PrintObjectConfig &object_config,
|
||||
const PrintConfig &print_config)
|
||||
{
|
||||
coord_t anchors_size = scale_(EXTERNAL_INFILL_MARGIN);
|
||||
|
||||
ExPolygons local_area = intersection_ex({surface.expolygon}, infill_area);
|
||||
ExPolygons anchors = intersection_ex(local_area, lower_slices_polygons);
|
||||
ExPolygons overhangs = diff_ex(local_area, lower_slices_polygons);
|
||||
if (overhangs.empty()) {
|
||||
return {};
|
||||
}
|
||||
Polygons anchors = intersection(infill_area, lower_slices_polygons);
|
||||
Polygons overhangs = diff(infill_area, lower_slices_polygons);
|
||||
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(),
|
||||
anchors_size * 0.33 + 0.5 * overhang_flow.scaled_spacing(),
|
||||
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) {
|
||||
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++) {
|
||||
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));
|
||||
anchor_areas_w_delta_anchor_size[i] = intersection_ex(anchor_areas_w_delta_anchor_size[i],
|
||||
offset_ex(clipped, deltas[i] + deltas[i + 1],
|
||||
anchor_areas_w_delta_anchor_size[i] = intersection(anchor_areas_w_delta_anchor_size[i],
|
||||
expand(clipped, deltas[i+1] + 0.1*overhang_flow.scaled_spacing(),
|
||||
EXTRA_PERIMETER_OFFSET_PARAMETERS));
|
||||
}
|
||||
|
||||
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(),
|
||||
anchor_areas_w_delta_anchor_size[i].end());
|
||||
inset_anchors = union_(inset_anchors, anchor_areas_w_delta_anchor_size[i]);
|
||||
}
|
||||
|
||||
inset_anchors = union_ex(inset_anchors);
|
||||
inset_anchors = closing_ex(to_polygons(inset_anchors), 1.0 * overhang_flow.scaled_spacing(), 1.0 * overhang_flow.scaled_spacing(),
|
||||
EXTRA_PERIMETER_OFFSET_PARAMETERS);
|
||||
inset_anchors = opening(inset_anchors, 0.8 * deltas[0], EXTRA_PERIMETER_OFFSET_PARAMETERS);
|
||||
inset_anchors = closing(inset_anchors, 0.8 * deltas[0], EXTRA_PERIMETER_OFFSET_PARAMETERS);
|
||||
|
||||
#ifdef EXTRA_PERIM_DEBUG_FILES
|
||||
{
|
||||
@ -832,58 +825,57 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
|
||||
#endif
|
||||
}
|
||||
|
||||
ExPolygons inset_overhang_area = diff_ex(local_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);
|
||||
Polygons inset_overhang_area = diff(infill_area, inset_anchors);
|
||||
|
||||
#ifdef EXTRA_PERIM_DEBUG_FILES
|
||||
{
|
||||
BoundingBox bbox = get_extents(inset_overhangs_w_anchors);
|
||||
BoundingBox bbox = get_extents(inset_overhang_area);
|
||||
bbox.offset(scale_(1.));
|
||||
::Slic3r::SVG svg(debug_out_path("inset_overhangs_w_anchors").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_overhang_area)) svg.draw(line, "red", scale_(0.20));
|
||||
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));
|
||||
::Slic3r::SVG svg(debug_out_path("inset_overhang_area").c_str(), bbox);
|
||||
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.15));
|
||||
svg.Close();
|
||||
}
|
||||
#endif
|
||||
|
||||
Polygons area_left_unfilled;
|
||||
Polygons inset_overhang_area_left_unfilled;
|
||||
|
||||
std::vector<std::vector<ExtrusionPaths>> extra_perims; // overhang region -> shell -> shell parts
|
||||
for (const ExPolygon &overhang : inset_overhang_area) {
|
||||
ExPolygons overhang_to_cover = {overhang};
|
||||
overhang_to_cover.insert(overhang_to_cover.end(), repeated_area.begin(), repeated_area.end());
|
||||
overhang_to_cover = union_ex(overhang_to_cover);
|
||||
for (const ExPolygon &overhang : union_ex(to_expolygons(inset_overhang_area))) {
|
||||
Polygons overhang_to_cover = to_polygons(overhang);
|
||||
Polygons expanded_overhang_to_cover = expand(overhang_to_cover, 1.1 * overhang_flow.scaled_spacing());
|
||||
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()) {
|
||||
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;
|
||||
}
|
||||
|
||||
extra_perims.emplace_back();
|
||||
std::vector<ExtrusionPaths> &overhang_region = extra_perims.back();
|
||||
|
||||
ExPolygons anchoring = intersection_ex({overhang_to_cover}, inset_anchors);
|
||||
ExPolygons perimeter_polygon = offset_ex(overhang_to_cover, -overhang_flow.scaled_spacing() * 0.6);
|
||||
Polygons anchoring = intersection(expanded_overhang_to_cover, inset_anchors);
|
||||
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);
|
||||
double unbridgeable_area = area(diff_ex(perimeter_polygon, {anchoring_convex_hull}));
|
||||
for (const ExPolygon &exp : perimeter_polygon) {
|
||||
for (const Polygon &hole : exp.holes) { unbridgeable_area += std::abs(area(hole)); }
|
||||
double unbridgeable_area = area(diff(real_overhang, {anchoring_convex_hull}));
|
||||
// penalize also holes
|
||||
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
|
||||
{
|
||||
BoundingBox bbox = get_extents(inset_overhang_area);
|
||||
BoundingBox bbox = get_extents(anchoring_convex_hull);
|
||||
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(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));
|
||||
@ -892,10 +884,8 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
|
||||
svg.Close();
|
||||
}
|
||||
#endif
|
||||
|
||||
auto total_length_of_real_overhang = total_length(to_polygons(real_overhang));
|
||||
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));
|
||||
if (unbridgeable_area < 0.2 * area(real_overhang) && unsupp_dist < total_length(real_overhang) * 0.125) {
|
||||
inset_overhang_area_left_unfilled.insert(inset_overhang_area_left_unfilled.end(),overhang_to_cover.begin(),overhang_to_cover.end());
|
||||
perimeter_polygon.clear();
|
||||
} else {
|
||||
// fill the overhang with perimeters
|
||||
@ -903,16 +893,15 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
|
||||
while (continuation_loops > 0) {
|
||||
auto prev = perimeter_polygon;
|
||||
// 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
|
||||
// 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_ex(perimeter_polygon);
|
||||
perimeter_polygon = intersection_ex({overhang_to_cover}, offset_ex(perimeter_polygon, -overhang_flow.scaled_spacing()));
|
||||
perimeter_polygon = union_(perimeter_polygon, anchoring);
|
||||
perimeter_polygon = intersection(offset(perimeter_polygon, -overhang_flow.scaled_spacing()), expanded_overhang_to_cover);
|
||||
|
||||
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()) {
|
||||
overhang_region.emplace_back();
|
||||
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;
|
||||
ExPolygons gap = shrinked.empty() ? 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) {
|
||||
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()) {
|
||||
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());
|
||||
}
|
||||
|
||||
if (intersection(perimeter_polygon, real_overhang).empty()) {
|
||||
continuation_loops--;
|
||||
}
|
||||
|
||||
if (intersection(perimeter_polygon, real_overhang).empty()) { continuation_loops--; }
|
||||
|
||||
if (prev == perimeter_polygon) {
|
||||
#ifdef EXTRA_PERIM_DEBUG_FILES
|
||||
@ -957,15 +945,14 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
|
||||
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();
|
||||
extrusion_paths_append(overhang_region.back(), perimeter, erOverhangPerimeter, overhang_flow.mm3_per_mm(),
|
||||
overhang_flow.width(), overhang_flow.height());
|
||||
|
||||
perimeter_polygon = offset_ex(perimeter_polygon, 0.5 * overhang_flow.scaled_spacing());
|
||||
perimeter_polygon.insert(perimeter_polygon.end(), anchoring.begin(), anchoring.end());
|
||||
perimeter_polygon = union_ex(perimeter_polygon);
|
||||
area_left_unfilled = union_(area_left_unfilled, to_polygons(perimeter_polygon));
|
||||
perimeter_polygon = expand(perimeter_polygon, 0.5 * overhang_flow.scaled_spacing());
|
||||
perimeter_polygon = union_(perimeter_polygon, anchoring);
|
||||
inset_overhang_area_left_unfilled.insert(inset_overhang_area_left_unfilled.end(), perimeter_polygon.begin(),perimeter_polygon.end());
|
||||
|
||||
#ifdef EXTRA_PERIM_DEBUG_FILES
|
||||
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(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(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();
|
||||
#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{};
|
||||
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
|
||||
BoundingBox bbox = get_extents(inset_overhang_area);
|
||||
bbox.offset(scale_(2.));
|
||||
::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_overhangs_w_anchors)) 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(inset_overhang_area_left_unfilled)) svg.draw(line, "blue", 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_overhang_area, inset_overhang_area_left_unfilled))) svg.draw(line, "yellow", scale_(0.05));
|
||||
svg.Close();
|
||||
#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
|
||||
@ -1215,31 +1204,23 @@ void PerimeterGenerator::process_arachne()
|
||||
// collapse too narrow infill areas
|
||||
const auto min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE));
|
||||
// append infill areas to fill_surfaces
|
||||
this->fill_surfaces->append(
|
||||
offset2_ex(
|
||||
union_ex(pp),
|
||||
float(- min_perimeter_infill_spacing / 2.),
|
||||
float(inset + min_perimeter_infill_spacing / 2.)),
|
||||
stInternal);
|
||||
// append infill areas to fill_surfaces
|
||||
ExPolygons infill_areas = offset2_ex(union_ex(pp), float(-min_perimeter_infill_spacing / 2.),
|
||||
float(inset + min_perimeter_infill_spacing / 2.));
|
||||
this->fill_surfaces->append(infill_areas, stInternal);
|
||||
|
||||
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) {
|
||||
// Generate extra perimeters on overhang areas, and cut them to these parts only, to save print time and material
|
||||
ExPolygons infill_area;
|
||||
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,
|
||||
auto [extra_perimeters, filled_area] = generate_extra_perimeters_over_overhangs(infill_areas,
|
||||
this->lower_slices_polygons(),
|
||||
this->overhang_flow, this->config->perimeters,
|
||||
this->m_scaled_resolution,
|
||||
this->overhang_flow, this->m_scaled_resolution,
|
||||
*this->object_config, *this->print_config);
|
||||
if (!extra_perimeters.empty()) {
|
||||
ExtrusionEntityCollection *this_islands_perimeters = static_cast<ExtrusionEntityCollection *>(this->loops->entities.back());
|
||||
ExtrusionEntityCollection new_perimeters{};
|
||||
new_perimeters.no_sort = this_islands_perimeters->no_sort;
|
||||
for (const ExtrusionPaths& paths : extra_perimeters) {
|
||||
new_perimeters.append(paths);
|
||||
}
|
||||
for (const ExtrusionPaths &paths : extra_perimeters) { new_perimeters.append(paths); }
|
||||
new_perimeters.append(this_islands_perimeters->entities);
|
||||
this_islands_perimeters->swap(new_perimeters);
|
||||
|
||||
@ -1516,34 +1497,25 @@ void PerimeterGenerator::process_classic()
|
||||
// collapse too narrow infill areas
|
||||
coord_t min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE));
|
||||
// append infill areas to fill_surfaces
|
||||
this->fill_surfaces->append(
|
||||
offset2_ex(
|
||||
union_ex(pp),
|
||||
float(- inset - min_perimeter_infill_spacing / 2.),
|
||||
float(min_perimeter_infill_spacing / 2.)),
|
||||
stInternal);
|
||||
ExPolygons infill_areas = offset2_ex(union_ex(pp), float(-inset - min_perimeter_infill_spacing / 2.),
|
||||
float(min_perimeter_infill_spacing / 2.));
|
||||
this->fill_surfaces->append(infill_areas, stInternal);
|
||||
|
||||
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) {
|
||||
// Generate extra perimeters on overhang areas, and cut them to these parts only, to save print time and material
|
||||
ExPolygons infill_area;
|
||||
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,
|
||||
auto [extra_perimeters, filled_area] = generate_extra_perimeters_over_overhangs(infill_areas,
|
||||
this->lower_slices_polygons(),
|
||||
this->overhang_flow, this->config->perimeters,
|
||||
this->m_scaled_resolution,
|
||||
this->overhang_flow, this->m_scaled_resolution,
|
||||
*this->object_config, *this->print_config);
|
||||
if (!extra_perimeters.empty()) {
|
||||
ExtrusionEntityCollection *this_islands_perimeters = static_cast<ExtrusionEntityCollection *>(this->loops->entities.back());
|
||||
ExtrusionEntityCollection new_perimeters{};
|
||||
new_perimeters.no_sort = this_islands_perimeters->no_sort;
|
||||
for (const ExtrusionPaths& paths : extra_perimeters) {
|
||||
new_perimeters.append(paths);
|
||||
}
|
||||
for (const ExtrusionPaths &paths : extra_perimeters) { new_perimeters.append(paths); }
|
||||
new_perimeters.append(this_islands_perimeters->entities);
|
||||
this_islands_perimeters->swap(new_perimeters);
|
||||
|
||||
|
||||
SurfaceCollection orig_surfaces = *this->fill_surfaces;
|
||||
this->fill_surfaces->clear();
|
||||
for (const auto &surface : orig_surfaces.surfaces) {
|
||||
|
@ -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,
|
||||
// or they are only notes not influencing the generated G-code.
|
||||
static std::unordered_set<std::string> steps_gcode = {
|
||||
"avoid_curled_filament_during_travels",
|
||||
"avoid_crossing_perimeters",
|
||||
"avoid_crossing_perimeters_max_detour",
|
||||
"bed_shape",
|
||||
@ -223,6 +222,8 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
||||
osteps.emplace_back(posInfill);
|
||||
osteps.emplace_back(posSupportMaterial);
|
||||
steps.emplace_back(psSkirtBrim);
|
||||
} else if (opt_key == "avoid_curled_filament_during_travels") {
|
||||
osteps.emplace_back(posEstimateCurledExtrusions);
|
||||
} else {
|
||||
// for legacy, if we can't handle this option let's invalidate all steps
|
||||
//FIXME invalidate all steps of all objects as well?
|
||||
|
@ -1231,13 +1231,13 @@ struct LayerCurlingEstimator
|
||||
|
||||
if (fabs(dist_from_prev_layer) < 2.0f * flow_width) {
|
||||
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) {
|
||||
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);
|
||||
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));
|
||||
} else {
|
||||
malformation_acc.reset();
|
||||
|
@ -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]
|
||||
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 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 min_distance_between_support_points = 3.0f; //mm
|
||||
|
@ -5174,6 +5174,8 @@ bool GLCanvas3D::_init_main_toolbar()
|
||||
item.left.toggable = true;
|
||||
item.left.render_callback = [this](float left, float right, float, float) {
|
||||
if (m_canvas != nullptr) {
|
||||
if (!m_canvas->HasFocus())
|
||||
m_canvas->SetFocus();
|
||||
if (_render_search_list(0.5f * (left + right)))
|
||||
_deactivate_search_toolbar_item();
|
||||
}
|
||||
|
@ -217,9 +217,7 @@ ObjectList::ObjectList(wxWindow* parent) :
|
||||
Bind(wxEVT_DATAVIEW_ITEM_DROP_POSSIBLE, &ObjectList::OnDropPossible, this);
|
||||
Bind(wxEVT_DATAVIEW_ITEM_DROP, &ObjectList::OnDrop, this);
|
||||
|
||||
#ifdef __WXMSW__
|
||||
Bind(wxEVT_DATAVIEW_ITEM_EDITING_STARTED, &ObjectList::OnEditingStarted, this);
|
||||
#endif /* __WXMSW__ */
|
||||
Bind(wxEVT_DATAVIEW_ITEM_EDITING_DONE, &ObjectList::OnEditingDone, this);
|
||||
|
||||
Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &ObjectList::ItemValueChanged, this);
|
||||
@ -1897,11 +1895,9 @@ bool ObjectList::del_subobject_item(wxDataViewItem& item)
|
||||
|
||||
// If last volume item with warning was deleted, unmark object item
|
||||
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());
|
||||
m_objects_model->UpdateWarningIcon(parent, icon_name);
|
||||
}
|
||||
else
|
||||
m_objects_model->Delete(item);
|
||||
|
||||
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))
|
||||
continue;
|
||||
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);
|
||||
if (obj->volumes.size() == 1) {
|
||||
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__
|
||||
// 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.
|
||||
void ObjectList::OnEditingStarted(wxDataViewEvent &event)
|
||||
{
|
||||
m_last_selected_column = -1;
|
||||
}
|
||||
#endif //__WXMSW__
|
||||
}
|
||||
|
||||
void ObjectList::OnEditingDone(wxDataViewEvent &event)
|
||||
{
|
||||
m_is_editing_started = false;
|
||||
if (event.GetColumn() != colName)
|
||||
return;
|
||||
|
||||
|
@ -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.
|
||||
int m_last_selected_column = -1;
|
||||
#endif /* __MSW__ */
|
||||
bool m_is_editing_started{ false };
|
||||
|
||||
#if 0
|
||||
SettingsFactory::Bundle m_freq_settings_fff;
|
||||
@ -406,6 +407,8 @@ public:
|
||||
void apply_volumes_order();
|
||||
bool has_paint_on_segmentation();
|
||||
|
||||
bool is_editing() const { return m_is_editing_started; }
|
||||
|
||||
private:
|
||||
#ifdef __WXOSX__
|
||||
// void OnChar(wxKeyEvent& event);
|
||||
@ -419,10 +422,8 @@ private:
|
||||
bool can_drop(const wxDataViewItem& item) const ;
|
||||
|
||||
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.
|
||||
void OnEditingStarted(wxDataViewEvent &event);
|
||||
#endif /* __WXMSW__ */
|
||||
void OnEditingDone(wxDataViewEvent &event);
|
||||
};
|
||||
|
||||
|
@ -75,14 +75,25 @@ static std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType t
|
||||
std::string ret;
|
||||
switch (type) {
|
||||
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::Circle: { ret = (hover_id == POINT_ID) ? _u8L("Center of circle") : _u8L("Point on circle"); break; }
|
||||
case Measure::SurfaceFeatureType::Edge: { ret = _u8L("Point on edge"); break; }
|
||||
case Measure::SurfaceFeatureType::Circle: { ret = _u8L("Point on circle"); break; }
|
||||
case Measure::SurfaceFeatureType::Plane: { ret = _u8L("Point on plane"); 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;
|
||||
}
|
||||
|
||||
static GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const std::vector<std::vector<int>>& planes_triangles, int idx)
|
||||
{
|
||||
assert(0 <= idx && idx < (int)planes_triangles.size());
|
||||
@ -251,7 +262,21 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event)
|
||||
m_mouse_left_down = 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()) {
|
||||
// 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) {
|
||||
SelectedFeatures selected_features_old = m_selected_features;
|
||||
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())
|
||||
item = m_selected_features.second;
|
||||
else {
|
||||
item = {
|
||||
(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
|
||||
};
|
||||
switch (m_mode)
|
||||
{
|
||||
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;
|
||||
};
|
||||
@ -284,8 +340,14 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event)
|
||||
m_selected_features.second.reset();
|
||||
else {
|
||||
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));
|
||||
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 {
|
||||
@ -296,8 +358,14 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event)
|
||||
else {
|
||||
const SelectedFeatures::Item item = item_from_feature();
|
||||
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));
|
||||
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()) {
|
||||
@ -330,11 +398,19 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event)
|
||||
// avoid closing the gizmo if the user clicks outside of any volume
|
||||
return true;
|
||||
}
|
||||
else if (mouse_event.RightDown() && mouse_event.CmdDown()) {
|
||||
else if (mouse_event.RightDown()) {
|
||||
// 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)) {
|
||||
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())
|
||||
m_mouse_left_down = false;
|
||||
|
||||
@ -369,21 +445,50 @@ void GLGizmoMeasure::data_changed()
|
||||
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)
|
||||
{
|
||||
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_curr_feature.has_value()) {
|
||||
m_mode = EMode::ExtendedSelection;
|
||||
if (activate_center_selection(SLAGizmoEventType::CtrlDown)) {
|
||||
m_mode = EMode::CenterSelection;
|
||||
disable_scene_raycasters();
|
||||
}
|
||||
}
|
||||
|
||||
m_ctrl_kar_filter.increase_count();
|
||||
}
|
||||
else if (action == SLAGizmoEventType::CtrlUp) {
|
||||
m_ctrl_kar_filter.reset_count();
|
||||
m_mode = EMode::BasicSelection;
|
||||
m_mode = control_down ? EMode::PointSelection : EMode::FeatureSelection;
|
||||
restore_scene_raycasters_state();
|
||||
}
|
||||
|
||||
@ -400,6 +505,7 @@ void GLGizmoMeasure::on_set_state()
|
||||
{
|
||||
if (m_state == Off) {
|
||||
m_ctrl_kar_filter.reset_count();
|
||||
m_shift_kar_filter.reset_count();
|
||||
m_curr_feature.reset();
|
||||
m_curr_point_on_feature_position.reset();
|
||||
restore_scene_raycasters_state();
|
||||
@ -408,7 +514,7 @@ void GLGizmoMeasure::on_set_state()
|
||||
m_measuring.reset();
|
||||
}
|
||||
else {
|
||||
m_mode = EMode::BasicSelection;
|
||||
m_mode = EMode::FeatureSelection;
|
||||
// store current state of scene raycaster for later use
|
||||
m_scene_raycasters.clear();
|
||||
auto scene_raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume);
|
||||
@ -467,7 +573,7 @@ void GLGizmoMeasure::on_render()
|
||||
Vec3f normal_on_model;
|
||||
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 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]() {
|
||||
if (m_last_inv_zoom != inv_zoom || m_last_circle != m_curr_feature) {
|
||||
@ -483,9 +589,19 @@ void GLGizmoMeasure::on_render()
|
||||
return false;
|
||||
};
|
||||
|
||||
if (m_mode == EMode::BasicSelection) {
|
||||
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_mode == EMode::FeatureSelection || m_mode == EMode::PointSelection) {
|
||||
if ((m_hover_id == SELECTION_1_ID && boost::algorithm::istarts_with(m_selected_features.first.source, _u8L("Center"))) ||
|
||||
(m_hover_id == SELECTION_2_ID && boost::algorithm::istarts_with(m_selected_features.second.source, _u8L("Center")))) {
|
||||
// Skip feature detection if hovering on a selected center
|
||||
m_curr_feature.reset();
|
||||
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_curr_point_on_feature_position.reset();
|
||||
}
|
||||
else {
|
||||
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);
|
||||
@ -537,7 +653,11 @@ void GLGizmoMeasure::on_render()
|
||||
}
|
||||
}
|
||||
}
|
||||
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 it = m_raycasters.find(feature_type_id);
|
||||
if (it != m_raycasters.end() && it->second != nullptr) {
|
||||
@ -554,6 +674,7 @@ void GLGizmoMeasure::on_render()
|
||||
return Vec3d::Zero();
|
||||
};
|
||||
|
||||
if (m_curr_feature.has_value()) {
|
||||
switch (m_curr_feature->get_type())
|
||||
{
|
||||
default: { assert(false); break; }
|
||||
@ -583,7 +704,7 @@ void GLGizmoMeasure::on_render()
|
||||
m_curr_point_on_feature_position = center;
|
||||
else {
|
||||
const Vec3d world_pof = position_on_feature(CIRCLE_ID, camera, [](const Vec3f& v) { return v; });
|
||||
const Eigen::Hyperplane<double, 3> plane(m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()* normal, m_volume_matrix * center);
|
||||
const Eigen::Hyperplane<double, 3> plane(m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal, m_volume_matrix * center);
|
||||
const Transform3d local_to_model_matrix = Geometry::translation_transform(center) * Eigen::Quaternion<double>::FromTwoVectors(Vec3d::UnitZ(), normal);
|
||||
const Vec3d local_proj = local_to_model_matrix.inverse() * m_volume_matrix.inverse() * plane.projection(world_pof);
|
||||
double angle = std::atan2(local_proj.y(), local_proj.x());
|
||||
@ -597,6 +718,7 @@ void GLGizmoMeasure::on_render()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (m_curr_feature.has_value() && m_curr_feature->get_type() == Measure::SurfaceFeatureType::Circle) {
|
||||
if (update_circle()) {
|
||||
@ -617,7 +739,6 @@ void GLGizmoMeasure::on_render()
|
||||
return;
|
||||
|
||||
shader->start_using();
|
||||
shader->set_uniform("emission_factor", 0.25f);
|
||||
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
|
||||
|
||||
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
|
||||
@ -634,8 +755,13 @@ void GLGizmoMeasure::on_render()
|
||||
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,
|
||||
float inv_zoom, bool update_raycasters_transform) {
|
||||
auto set_emission_uniform = [this, shader](const ColorRGBA& color, bool hover) {
|
||||
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())
|
||||
{
|
||||
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 Transform3d feature_matrix = Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom);
|
||||
set_matrix_uniforms(feature_matrix);
|
||||
set_emission_uniform(colors.front(), hover);
|
||||
m_sphere.model.set_color(colors.front());
|
||||
m_sphere.model.render();
|
||||
if (update_raycasters_transform) {
|
||||
@ -657,20 +784,23 @@ void GLGizmoMeasure::on_render()
|
||||
{
|
||||
const auto& [center, radius, normal] = feature.get_circle();
|
||||
// render center
|
||||
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();
|
||||
if (update_raycasters_transform) {
|
||||
auto it = m_raycasters.find(POINT_ID);
|
||||
if (it != m_raycasters.end() && it->second != nullptr)
|
||||
it->second->set_transform(center_matrix);
|
||||
}
|
||||
// render circle
|
||||
if (m_mode != EMode::CenterSelection) {
|
||||
const Transform3d circle_matrix = Transform3d::Identity();
|
||||
set_matrix_uniforms(circle_matrix);
|
||||
if (update_raycasters_transform) {
|
||||
set_emission_uniform(colors.back(), hover);
|
||||
m_circle.model.set_color(colors.back());
|
||||
m_circle.model.render();
|
||||
auto it = m_raycasters.find(CIRCLE_ID);
|
||||
@ -681,35 +811,40 @@ void GLGizmoMeasure::on_render()
|
||||
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>());
|
||||
circle.init_from(std::move(circle_geometry));
|
||||
set_emission_uniform(colors.back(), hover);
|
||||
circle.set_color(colors.back());
|
||||
circle.render();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Measure::SurfaceFeatureType::Edge:
|
||||
{
|
||||
const auto& [from, to] = feature.get_edge();
|
||||
// render extra point
|
||||
if (update_raycasters_transform) {
|
||||
const std::optional<Vec3d> extra = feature.get_extra_point();
|
||||
if (extra.has_value()) {
|
||||
const Vec3d extra_world = TransformHelper::model_to_world(*extra, m_volume_matrix);
|
||||
const Transform3d point_matrix = Geometry::translation_transform(extra_world) * Geometry::scale_transform(inv_zoom);
|
||||
set_matrix_uniforms(point_matrix);
|
||||
set_emission_uniform(colors.front(), hover);
|
||||
m_sphere.model.set_color(colors.front());
|
||||
m_sphere.model.render();
|
||||
if (update_raycasters_transform) {
|
||||
auto it = m_raycasters.find(POINT_ID);
|
||||
if (it != m_raycasters.end() && it->second != nullptr)
|
||||
it->second->set_transform(point_matrix);
|
||||
}
|
||||
}
|
||||
// render edge
|
||||
if (m_mode != EMode::CenterSelection) {
|
||||
const Vec3d from_world = TransformHelper::model_to_world(from, m_volume_matrix);
|
||||
const Vec3d to_world = TransformHelper::model_to_world(to, m_volume_matrix);
|
||||
const Transform3d edge_matrix = Geometry::translation_transform(from_world) *
|
||||
Eigen::Quaternion<double>::FromTwoVectors(Vec3d::UnitZ(), to_world - from_world) *
|
||||
Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (to_world - from_world).norm() });
|
||||
set_matrix_uniforms(edge_matrix);
|
||||
set_emission_uniform(colors.back(), hover);
|
||||
m_cylinder.model.set_color(colors.back());
|
||||
m_cylinder.model.render();
|
||||
if (update_raycasters_transform) {
|
||||
@ -717,6 +852,7 @@ void GLGizmoMeasure::on_render()
|
||||
if (it != m_raycasters.end() && it->second != nullptr)
|
||||
it->second->set_transform(edge_matrix);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Measure::SurfaceFeatureType::Plane:
|
||||
@ -724,6 +860,7 @@ void GLGizmoMeasure::on_render()
|
||||
const auto& [idx, normal, pt] = feature.get_plane();
|
||||
assert(idx < m_plane_models_cache.size());
|
||||
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].render();
|
||||
if (update_raycasters_transform) {
|
||||
@ -737,19 +874,19 @@ void GLGizmoMeasure::on_render()
|
||||
};
|
||||
|
||||
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]() {
|
||||
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()) {
|
||||
std::vector<ColorRGBA> colors;
|
||||
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)
|
||||
colors.emplace_back(SELECTED_2ND_COLOR);
|
||||
colors.emplace_back(hovering_color());
|
||||
else {
|
||||
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)) {
|
||||
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) {
|
||||
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; });
|
||||
@ -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)) {
|
||||
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) {
|
||||
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; });
|
||||
@ -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) {
|
||||
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);
|
||||
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();
|
||||
}
|
||||
}
|
||||
@ -1343,12 +1482,15 @@ void GLGizmoMeasure::render_debug_dialog()
|
||||
{
|
||||
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;
|
||||
}
|
||||
case Measure::SurfaceFeatureType::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_pt2", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text));
|
||||
break;
|
||||
@ -1356,6 +1498,8 @@ void GLGizmoMeasure::render_debug_dialog()
|
||||
case Measure::SurfaceFeatureType::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_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));
|
||||
@ -1364,6 +1508,10 @@ void GLGizmoMeasure::render_debug_dialog()
|
||||
case Measure::SurfaceFeatureType::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_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));
|
||||
@ -1376,6 +1524,28 @@ void GLGizmoMeasure::render_debug_dialog()
|
||||
};
|
||||
|
||||
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())
|
||||
m_imgui->text("Empty selection");
|
||||
else {
|
||||
@ -1402,7 +1572,7 @@ void GLGizmoMeasure::render_debug_dialog()
|
||||
void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit)
|
||||
{
|
||||
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 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;
|
||||
ColorRGBA color;
|
||||
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");
|
||||
else if (m_hover_id == SELECTION_2_ID)
|
||||
text = _u8L("Unselect point");
|
||||
text = (m_mode == EMode::CenterSelection) ? _u8L("Unselect center") : _u8L("Unselect point");
|
||||
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;
|
||||
}
|
||||
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)
|
||||
text = _u8L("Unselect feature");
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -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()) {
|
||||
add_strings_row_to_table(*m_imgui, CTRL_STR + "+" + _u8L("Right mouse button"), ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Restart selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text));
|
||||
if (m_mode == EMode::FeatureSelection && m_hover_id != -1) {
|
||||
add_strings_row_to_table(*m_imgui, _u8L("Shift"), ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text));
|
||||
++row_count;
|
||||
}
|
||||
|
||||
if (m_mode == EMode::BasicSelection && m_hover_id != -1) {
|
||||
add_strings_row_to_table(*m_imgui, CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text));
|
||||
if (m_mode != EMode::CenterSelection && feature_has_center(m_curr_feature)) {
|
||||
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;
|
||||
}
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
@ -1492,16 +1669,22 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit
|
||||
//bool data_text_set = false;
|
||||
//ImGui::Separator();
|
||||
//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));
|
||||
// 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()) {
|
||||
// m_imgui->text(point_on_feature_type_as_string(feature_type, m_hover_id));
|
||||
// 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)
|
||||
// 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;
|
||||
//unsigned int data_row_count = 0;
|
||||
//if (ImGui::BeginTable("Data", 2)) {
|
||||
// if (m_mode == EMode::BasicSelection) {
|
||||
// if (m_mode == EMode::FeatureSelection) {
|
||||
// switch (feature_type)
|
||||
// {
|
||||
// default: { break; }
|
||||
|
@ -23,8 +23,9 @@ class GLGizmoMeasure : public GLGizmoBase
|
||||
{
|
||||
enum class EMode : unsigned char
|
||||
{
|
||||
BasicSelection,
|
||||
ExtendedSelection
|
||||
FeatureSelection,
|
||||
PointSelection,
|
||||
CenterSelection
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
std::unique_ptr<Measure::Measuring> m_measuring; // PIMPL
|
||||
@ -114,6 +115,7 @@ class GLGizmoMeasure : public GLGizmoBase
|
||||
Vec2d m_mouse_pos{ Vec2d::Zero() };
|
||||
|
||||
KeyAutoRepeatFilter m_ctrl_kar_filter;
|
||||
KeyAutoRepeatFilter m_shift_kar_filter;
|
||||
|
||||
SelectedFeatures m_selected_features;
|
||||
bool m_pending_scale{ false };
|
||||
|
@ -26,6 +26,7 @@ enum class SLAGizmoEventType : unsigned char {
|
||||
SelectAll,
|
||||
CtrlDown,
|
||||
CtrlUp,
|
||||
ShiftDown,
|
||||
ShiftUp,
|
||||
AltUp,
|
||||
ApplyChanges,
|
||||
|
@ -608,10 +608,8 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
|
||||
const int keyCode = evt.GetKeyCode();
|
||||
bool processed = false;
|
||||
|
||||
if (evt.GetEventType() == wxEVT_KEY_UP)
|
||||
{
|
||||
if (m_current == SlaSupports || m_current == Hollow || m_current == Cut)
|
||||
{
|
||||
if (evt.GetEventType() == wxEVT_KEY_UP) {
|
||||
if (m_current == SlaSupports || m_current == Hollow || m_current == Cut) {
|
||||
GLGizmoBase* gizmo = get_current();
|
||||
const bool is_editing = m_current == Hollow ? true : gizmo->is_in_editing_mode();
|
||||
const bool is_rectangle_dragging = gizmo->is_selection_rectangle_dragging();
|
||||
@ -627,18 +625,19 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
|
||||
processed = true;
|
||||
}
|
||||
}
|
||||
else if (m_current == Measure && keyCode == WXK_CONTROL) {
|
||||
gizmo_event(SLAGizmoEventType::CtrlUp, Vec2d::Zero(), false);
|
||||
else if (m_current == Measure) {
|
||||
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)
|
||||
// m_parent.set_cursor(GLCanvas3D::Standard);
|
||||
}
|
||||
else if (evt.GetEventType() == wxEVT_KEY_DOWN)
|
||||
{
|
||||
if ((m_current == SlaSupports) && ((keyCode == WXK_SHIFT) || (keyCode == WXK_ALT))
|
||||
&& get_current()->is_in_editing_mode())
|
||||
{
|
||||
else if (evt.GetEventType() == wxEVT_KEY_DOWN) {
|
||||
if (m_current == SlaSupports && (keyCode == WXK_SHIFT || keyCode == WXK_ALT)
|
||||
&& get_current()->is_in_editing_mode()) {
|
||||
// m_parent.set_cursor(GLCanvas3D::Cross);
|
||||
processed = true;
|
||||
}
|
||||
@ -664,8 +663,11 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
|
||||
if (simplify != nullptr)
|
||||
processed = simplify->on_esc_key_down();
|
||||
}
|
||||
else if (m_current == Measure && keyCode == WXK_CONTROL) {
|
||||
gizmo_event(SLAGizmoEventType::CtrlDown, Vec2d::Zero(), true);
|
||||
else if (m_current == Measure) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4890,12 +4890,12 @@ void Plater::priv::set_bed_shape(const Pointfs& shape, const double max_print_he
|
||||
|
||||
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
|
||||
{
|
||||
return !model.objects.empty();
|
||||
return !model.objects.empty() && !sidebar->obj_list()->is_editing();
|
||||
}
|
||||
|
||||
bool Plater::priv::can_fix_through_netfabb() const
|
||||
|
Loading…
Reference in New Issue
Block a user