Fixed conflicts after merge with master

This commit is contained in:
enricoturri1966 2023-04-26 12:35:02 +02:00
commit 159e699cd5
155 changed files with 5052 additions and 1623 deletions

View File

@ -406,6 +406,7 @@ endif()
set(TBB_DEBUG 1) set(TBB_DEBUG 1)
find_package(TBB REQUIRED) find_package(TBB REQUIRED)
slic3r_remap_configs(TBB::tbb RelWithDebInfo Release) slic3r_remap_configs(TBB::tbb RelWithDebInfo Release)
slic3r_remap_configs(TBB::tbbmalloc RelWithDebInfo Release)
# include_directories(${TBB_INCLUDE_DIRS}) # include_directories(${TBB_INCLUDE_DIRS})
# add_definitions(${TBB_DEFINITIONS}) # add_definitions(${TBB_DEFINITIONS})
# if(MSVC) # if(MSVC)

View File

@ -1,3 +1,7 @@
min_slic3r_version = 2.6.0-alpha4
0.2.4 Enable pad for Anycubic SLA profiles
0.2.3 Added Photon Mono printer.
0.2.2 Added Photon Mono SE printer.
min_slic3r_version = 2.6.0-alpha2 min_slic3r_version = 2.6.0-alpha2
0.2.1 Added Eolas Prints filaments. 0.2.1 Added Eolas Prints filaments.
0.2.0 Added Photon Mono X printer. 0.2.0 Added Photon Mono X printer.

View File

@ -73,6 +73,13 @@ technology = FFF
family = PREDATOR family = PREDATOR
default_materials = Generic PLA @PREDATOR; Generic PETG @PREDATOR; Generic ABS @PREDATOR default_materials = Generic PLA @PREDATOR; Generic PETG @PREDATOR; Generic ABS @PREDATOR
[printer_model:PHOTON MONO]
name = Photon Mono
variants = default
technology = SLA
family = PHOTON MONO
default_materials = Generic Blue Resin @MONO 0.05
[printer_model:PHOTON MONO X] [printer_model:PHOTON MONO X]
name = Photon Mono X name = Photon Mono X
variants = default variants = default
@ -80,6 +87,13 @@ technology = SLA
family = PHOTON MONO family = PHOTON MONO
default_materials = Generic Blue Resin @MONO 0.05 default_materials = Generic Blue Resin @MONO 0.05
[printer_model:PHOTON MONO SE]
name = Photon Mono SE
variants = default
technology = SLA
family = PHOTON MONO
default_materials = Generic Blue Resin @MONO 0.05
# All presets starting with asterisk, for example *common*, are intermediate and they will # All presets starting with asterisk, for example *common*, are intermediate and they will
# not make it into the user interface. # not make it into the user interface.
@ -2327,11 +2341,10 @@ z_offset = 0
## SLA printers ## SLA printers
[sla_print:*common print ANYCUBIC SLA*] [sla_print:*common print ANYCUBIC SLA*]
compatible_printers_condition = printer_notes=~/.*PHOTONMONOX.*/ compatible_printers_condition = printer_notes=~/.*VENDOR_ANYCUBIC.*/ and printer_notes=~/.*SLA.*/
layer_height = 0.05 layer_height = 0.05
output_filename_format = [input_filename_base].pwmx
pad_edge_radius = 0.5 pad_edge_radius = 0.5
pad_enable = 0 pad_enable = 1
pad_max_merge_distance = 50 pad_max_merge_distance = 50
pad_wall_height = 0 pad_wall_height = 0
pad_wall_thickness = 1 pad_wall_thickness = 1
@ -2355,20 +2368,38 @@ support_pillar_widening_factor = 0
supports_enable = 1 supports_enable = 1
support_small_pillar_diameter_percent = 60% support_small_pillar_diameter_percent = 60%
[sla_print:0.05 Normal @ANYCUBIC] [sla_print:0.05 Normal @ANYCUBIC ABSTRACT]
inherits = *common print ANYCUBIC SLA* inherits = *common print ANYCUBIC SLA*
compatible_printers_condition = printer_notes=~/.*ABSTRACT_ONLY.*/
layer_height = 0.05 layer_height = 0.05
[sla_print:0.05 Normal @ANYCUBIC MONO]
inherits = 0.05 Normal @ANYCUBIC ABSTRACT
compatible_printers_condition = printer_notes=~/.*PHOTONMONO\n.*/
output_filename_format = [input_filename_base].pwmo
[sla_print:0.05 Normal @ANYCUBIC MONO X]
inherits = 0.05 Normal @ANYCUBIC ABSTRACT
compatible_printers_condition = printer_notes=~/.*PHOTONMONOX\n.*/
output_filename_format = [input_filename_base].pwmx
[sla_print:0.05 Normal @ANYCUBIC MONO SE]
inherits = 0.05 Normal @ANYCUBIC ABSTRACT
compatible_printers_condition = printer_notes=~/.*PHOTONMONOSE\n.*/
output_filename_format = [input_filename_base].pwma
## SLA materials ## SLA materials
#MONO series printer need a significantly reduced exposure time but are otherwise compatible
[sla_material:*common ANYCUBIC SLA*] [sla_material:*common ANYCUBIC SLA*]
compatible_printers_condition = printer_notes=~/.*PHOTONMONOX.*/ compatible_printers_condition = printer_notes=~/.*VENDOR_ANYCUBIC.*/ and printer_notes=~/.*SLA.*/
compatible_prints_condition = layer_height == 0.05 compatible_prints_condition = layer_height == 0.05
exposure_time = 7 exposure_time = 7
initial_exposure_time = 40 initial_exposure_time = 40
initial_layer_height = 0.05 initial_layer_height = 0.05
material_correction = 1,1,1 material_correction = 1,1,1
material_notes = LIFT_DISTANCE=8.0\nLIFT_SPEED=2.5\nRETRACT_SPEED=3.0\nBOTTOM_LIFT_SPEED=2.0\nBOTTOM_LIFT_DISTANCE=9.0\nDELAY_BEFORE_EXPOSURE=0.5 material_notes = #Distances are defined in mm, speeds are defined in mm/s.\n#Delay is defined in s.\nLIFT_DISTANCE=8.0\nLIFT_SPEED=2.5\nRETRACT_SPEED=3.0\nBOTTOM_LIFT_SPEED=2.0\nBOTTOM_LIFT_DISTANCE=9.0\nDELAY_BEFORE_EXPOSURE=0.5\nANTIALIASING=1
[sla_material:*common 0.05 ANYCUBIC SLA*] [sla_material:*common 0.05 ANYCUBIC SLA*]
inherits = *common ANYCUBIC SLA* inherits = *common ANYCUBIC SLA*
@ -2380,10 +2411,66 @@ initial_exposure_time = 40
material_type = Tough material_type = Tough
material_vendor = Generic material_vendor = Generic
material_colour = #6080EC material_colour = #6080EC
compatible_printers_condition = printer_notes=~/.*PHOTONMONOX.*/ compatible_printers_condition = printer_notes=~/.*MONO.*/ and printer_notes=~/.*VENDOR_ANYCUBIC.*/ and printer_notes=~/.*SLA.*/
## Printers ## Printers
[printer:Anycubic Photon Mono]
printer_technology = SLA
printer_model = PHOTON MONO
printer_variant = default
default_sla_material_profile = Generic Blue Resin @MONO 0.05
default_sla_print_profile = 0.05 Normal @ANYCUBIC
thumbnails = 224x168
sla_archive_format = pwmo
bed_shape = 0x0,82.62x0,82.62x130.56,0x130.56
display_orientation = landscape
display_mirror_x = 1
display_mirror_y = 0
display_pixels_x = 1620
display_pixels_y = 2560
display_width = 82.62
display_height = 130.56
max_print_height = 165
elefant_foot_compensation = 0.2
elefant_foot_min_width = 0.2
min_exposure_time = 0.8
max_exposure_time = 120
min_initial_exposure_time = 0.8
max_initial_exposure_time = 300
printer_correction = 1,1,1
gamma_correction = 1
area_fill = 50
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.'\nPRINTER_VENDOR_ANYCUBIC\nPRINTER_MODEL_PHOTONMONO\nPRINTER_TECHNOLOGY_SLA\n
[printer:Anycubic Photon Mono SE]
printer_technology = SLA
printer_model = PHOTON MONO SE
printer_variant = default
default_sla_material_profile = Generic Blue Resin @MONO 0.05
default_sla_print_profile = 0.05 Normal @ANYCUBIC
thumbnails = 224x168
sla_archive_format = pwms
bed_shape = 0x0,82.62x0,82.62x130.56,0x130.56
display_orientation = landscape
display_mirror_x = 1
display_mirror_y = 0
display_pixels_x = 1620
display_pixels_y = 2560
display_width = 82.62
display_height = 130.56
max_print_height = 160
elefant_foot_compensation = 0.2
elefant_foot_min_width = 0.2
min_exposure_time = 0.8
max_exposure_time = 120
min_initial_exposure_time = 0.8
max_initial_exposure_time = 300
printer_correction = 1,1,1
gamma_correction = 1
area_fill = 45
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.'\nPRINTER_VENDOR_ANYCUBIC\nPRINTER_MODEL_PHOTONMONOSE\nPRINTER_TECHNOLOGY_SLA\n
[printer:Anycubic Photon Mono X] [printer:Anycubic Photon Mono X]
printer_technology = SLA printer_technology = SLA
printer_model = PHOTON MONO X printer_model = PHOTON MONO X
@ -2410,4 +2497,4 @@ max_initial_exposure_time = 300
printer_correction = 1,1,1 printer_correction = 1,1,1
gamma_correction = 1 gamma_correction = 1
area_fill = 45 area_fill = 45
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.'\nPRINTER_VENDOR_ANYCUBIC\nPRINTER_MODEL_PHOTONMONOX\n printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.'\nPRINTER_VENDOR_ANYCUBIC\nPRINTER_MODEL_PHOTONMONOX\nPRINTER_TECHNOLOGY_SLA\n

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -1,4 +1,7 @@
min_slic3r_version = 2.6.0-alpha5 min_slic3r_version = 2.6.0-alpha5
1.9.0-alpha4 Updated XL and MK4 profiles. Updated PC Blend Carbon Fiber density.
1.9.0-alpha3 Updated compatibility condition for MMU1 filaments.
1.9.0-alpha2 Added profiles for Spectrum filaments.
1.9.0-alpha1 Added profiles for Original Prusa MK4. 1.9.0-alpha1 Added profiles for Original Prusa MK4.
1.9.0-alpha0 Updated output filename format. 1.9.0-alpha0 Updated output filename format.
1.7.0-alpha2 Updated compatibility condition in some filament profiles (Prusa XL). 1.7.0-alpha2 Updated compatibility condition in some filament profiles (Prusa XL).
@ -8,7 +11,13 @@ min_slic3r_version = 2.6.0-alpha1
1.6.0-alpha2 Added profile for Prusament PETG Carbon Fiber and Fiberthree F3 PA-GF30 Pro. Updated acceleration settings for Prusa MINI. 1.6.0-alpha2 Added profile for Prusament PETG Carbon Fiber and Fiberthree F3 PA-GF30 Pro. Updated acceleration settings for Prusa MINI.
1.6.0-alpha1 Updated FW version notification. Decreased min layer time for PLA. 1.6.0-alpha1 Updated FW version notification. Decreased min layer time for PLA.
1.6.0-alpha0 Default top fill set to monotonic lines. Updated infill/perimeter overlap values. Updated output filename format. Enabled dynamic overhang speeds. 1.6.0-alpha0 Default top fill set to monotonic lines. Updated infill/perimeter overlap values. Updated output filename format. Enabled dynamic overhang speeds.
min_slic3r_version = 2.5.2-rc0
1.7.3 Updated XL and MK4 profiles. Updated PC Blend Carbon Fiber density.
1.7.2 Updated compatibility condition for MMU1 filaments.
1.7.1 Added SLA materials. Updated MK4 and XL profiles.
1.7.0 Added profiles for Original Prusa MK4.
min_slic3r_version = 2.5.1-rc0 min_slic3r_version = 2.5.1-rc0
1.6.4 Fixed compatibility condition for MMU1 filaments.
1.6.3 Added SLA materials. 1.6.3 Added SLA materials.
1.6.2 Updated compatibility condition in some filament profiles (Prusa XL). 1.6.2 Updated compatibility condition in some filament profiles (Prusa XL).
1.6.1 Added filament profile for Prusament PETG Tungsten 75%. Updated Prusa XL profiles. 1.6.1 Added filament profile for Prusament PETG Tungsten 75%. Updated Prusa XL profiles.

View File

@ -5,7 +5,7 @@
name = Prusa Research name = Prusa Research
# Configuration version of this file. Config file will only be installed, if the config_version differs. # Configuration version of this file. Config file will only be installed, if the config_version differs.
# This means, the server may force the PrusaSlicer configuration to be downgraded. # This means, the server may force the PrusaSlicer configuration to be downgraded.
config_version = 1.9.0-alpha1 config_version = 1.9.0-alpha4
# Where to get the updates from? # Where to get the updates from?
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/ config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/
changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1% changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1%
@ -2130,6 +2130,7 @@ default_acceleration = 1250
max_print_speed = 200 max_print_speed = 200
first_layer_extrusion_width = 0.5 first_layer_extrusion_width = 0.5
support_material_extrusion_width = 0.37 support_material_extrusion_width = 0.37
top_infill_extrusion_width = 0.4
gcode_resolution = 0.008 gcode_resolution = 0.008
compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.4 compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.4
@ -2162,6 +2163,7 @@ max_print_speed = 200
first_layer_extrusion_width = 0.5 first_layer_extrusion_width = 0.5
gcode_resolution = 0.008 gcode_resolution = 0.008
support_material_extrusion_width = 0.37 support_material_extrusion_width = 0.37
top_infill_extrusion_width = 0.4
compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.4 compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.4
[print:0.20mm SPEED @XL 0.4] [print:0.20mm SPEED @XL 0.4]
@ -2192,6 +2194,7 @@ default_acceleration = 1250
max_print_speed = 200 max_print_speed = 200
first_layer_extrusion_width = 0.5 first_layer_extrusion_width = 0.5
support_material_extrusion_width = 0.37 support_material_extrusion_width = 0.37
top_infill_extrusion_width = 0.42
compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.4 compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.4
[print:0.30mm DRAFT @XL 0.4] [print:0.30mm DRAFT @XL 0.4]
@ -3036,7 +3039,7 @@ inherits = *0.15mm*; *MK4*
perimeter_speed = 45 perimeter_speed = 45
external_perimeter_speed = 25 external_perimeter_speed = 25
small_perimeter_speed = 25 small_perimeter_speed = 25
infill_speed = 90 infill_speed = 120
solid_infill_speed = 90 solid_infill_speed = 90
top_solid_infill_speed = 40 top_solid_infill_speed = 40
support_material_contact_distance = 0.2 support_material_contact_distance = 0.2
@ -3059,6 +3062,7 @@ max_print_speed = 200
first_layer_extrusion_width = 0.5 first_layer_extrusion_width = 0.5
support_material_extrusion_width = 0.37 support_material_extrusion_width = 0.37
gcode_resolution = 0.008 gcode_resolution = 0.008
top_infill_extrusion_width = 0.4
compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.4 compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.4
[print:0.15mm SPEED @MK4 0.4] [print:0.15mm SPEED @MK4 0.4]
@ -3089,6 +3093,7 @@ max_print_speed = 200
first_layer_extrusion_width = 0.5 first_layer_extrusion_width = 0.5
support_material_extrusion_width = 0.37 support_material_extrusion_width = 0.37
gcode_resolution = 0.008 gcode_resolution = 0.008
top_infill_extrusion_width = 0.42
compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.4 compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.4
[print:0.20mm QUALITY @MK4 0.4] [print:0.20mm QUALITY @MK4 0.4]
@ -3096,7 +3101,7 @@ inherits = *0.20mm*; *MK4*
perimeter_speed = 45 perimeter_speed = 45
external_perimeter_speed = 25 external_perimeter_speed = 25
small_perimeter_speed = 25 small_perimeter_speed = 25
infill_speed = 90 infill_speed = 120
solid_infill_speed = 90 solid_infill_speed = 90
top_solid_infill_speed = 40 top_solid_infill_speed = 40
support_material_contact_distance = 0.2 support_material_contact_distance = 0.2
@ -3119,6 +3124,7 @@ max_print_speed = 200
first_layer_extrusion_width = 0.5 first_layer_extrusion_width = 0.5
gcode_resolution = 0.008 gcode_resolution = 0.008
support_material_extrusion_width = 0.37 support_material_extrusion_width = 0.37
top_infill_extrusion_width = 0.4
compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.4 compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.4
[print:0.20mm SPEED @MK4 0.4] [print:0.20mm SPEED @MK4 0.4]
@ -3149,6 +3155,7 @@ default_acceleration = 1000
max_print_speed = 200 max_print_speed = 200
first_layer_extrusion_width = 0.5 first_layer_extrusion_width = 0.5
support_material_extrusion_width = 0.37 support_material_extrusion_width = 0.37
top_infill_extrusion_width = 0.42
compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.4 compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.4
[print:0.30mm DRAFT @MK4 0.4] [print:0.30mm DRAFT @MK4 0.4]
@ -3565,7 +3572,7 @@ compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.8
cooling = 1 cooling = 1
compatible_printers = compatible_printers =
# For now, all but selected filaments are disabled for the MMU 2.0 # For now, all but selected filaments are disabled for the MMU 2.0
compatible_printers_condition = ! single_extruder_multi_material and printer_notes!~/.*PG.*/ compatible_printers_condition = ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) and printer_notes!~/.*PG.*/
end_filament_gcode = "; Filament-specific end gcode" end_filament_gcode = "; Filament-specific end gcode"
extrusion_multiplier = 1 extrusion_multiplier = 1
filament_loading_speed = 28 filament_loading_speed = 28
@ -3608,7 +3615,7 @@ min_fan_speed = 100
temperature = 210 temperature = 210
slowdown_below_layer_time = 10 slowdown_below_layer_time = 10
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.01{elsif nozzle_diameter[0]==0.6}0.04{else}0.05{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 K18{elsif nozzle_diameter[0]==0.8};{else}M900 K30{endif} ; Filament gcode LA 1.0" 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.01{elsif nozzle_diameter[0]==0.6}0.04{else}0.05{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 K18{elsif nozzle_diameter[0]==0.8};{else}M900 K30{endif} ; Filament gcode LA 1.0"
compatible_printers_condition = ! single_extruder_multi_material and printer_notes!~/.*PG.*/ compatible_printers_condition = ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) and printer_notes!~/.*PG.*/
[filament:*PLAPG*] [filament:*PLAPG*]
start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.06{elsif nozzle_diameter[0]==0.25}0.14{elsif nozzle_diameter[0]==0.3}0.08{elsif nozzle_diameter[0]==0.35}0.07{elsif nozzle_diameter[0]==0.6}0.03{elsif nozzle_diameter[0]==0.5}0.035{elsif nozzle_diameter[0]==0.8}0.02{else}0{endif} ; Filament gcode\n\nM142 S36 ; set heatbreak target temp" start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.06{elsif nozzle_diameter[0]==0.25}0.14{elsif nozzle_diameter[0]==0.3}0.08{elsif nozzle_diameter[0]==0.35}0.07{elsif nozzle_diameter[0]==0.6}0.03{elsif nozzle_diameter[0]==0.5}0.035{elsif nozzle_diameter[0]==0.8}0.02{else}0{endif} ; Filament gcode\n\nM142 S36 ; set heatbreak target temp"
@ -3658,7 +3665,7 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no
temperature = 240 temperature = 240
filament_retract_length = 1 filament_retract_length = 1
filament_retract_lift = 0.2 filament_retract_lift = 0.2
compatible_printers_condition = printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material compatible_printers_condition = printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
[filament:*PET06*] [filament:*PET06*]
inherits = *PET* inherits = *PET*
@ -3700,7 +3707,7 @@ slowdown_below_layer_time = 18
filament_retract_length = 0.8 filament_retract_length = 0.8
[filament:*04PLUS*] [filament:*04PLUS*]
compatible_printers_condition = nozzle_diameter[0]>=0.4 and ! single_extruder_multi_material and printer_notes!~/.*PG.*/ compatible_printers_condition = nozzle_diameter[0]>=0.4 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) and printer_notes!~/.*PG.*/
[filament:*04PLUSPG*] [filament:*04PLUSPG*]
compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/
@ -3815,7 +3822,7 @@ compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.*
[filament:*ABSPG*] [filament:*ABSPG*]
compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8
filament_max_volumetric_speed = 14 filament_max_volumetric_speed = 12
start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.04{elsif nozzle_diameter[0]==0.25}0.1{elsif nozzle_diameter[0]==0.3}0.06{elsif nozzle_diameter[0]==0.35}0.05{elsif nozzle_diameter[0]==0.5}0.03{elsif nozzle_diameter[0]==0.6}0.02{elsif nozzle_diameter[0]==0.8}0.01{else}0{endif} ; Filament gcode\n\nM142 S40 ; set heatbreak target temp" start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.04{elsif nozzle_diameter[0]==0.25}0.1{elsif nozzle_diameter[0]==0.3}0.06{elsif nozzle_diameter[0]==0.35}0.05{elsif nozzle_diameter[0]==0.5}0.03{elsif nozzle_diameter[0]==0.6}0.02{elsif nozzle_diameter[0]==0.8}0.01{else}0{endif} ; Filament gcode\n\nM142 S40 ; set heatbreak target temp"
filament_cooling_final_speed = 50 filament_cooling_final_speed = 50
filament_cooling_initial_speed = 10 filament_cooling_initial_speed = 10
@ -3869,7 +3876,7 @@ compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.6
[filament:*PC08PG*] [filament:*PC08PG*]
inherits = *PCPG* inherits = *PCPG*
filament_max_volumetric_speed = 20 filament_max_volumetric_speed = 18
compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.8 compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.8
[filament:*PCMK4*] [filament:*PCMK4*]
@ -3885,7 +3892,7 @@ compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.6
[filament:*PC08MK4*] [filament:*PC08MK4*]
inherits = *PCMK4* inherits = *PCMK4*
filament_max_volumetric_speed = 20 filament_max_volumetric_speed = 18
compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.8 compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.8
[filament:*PAPG*] [filament:*PAPG*]
@ -4196,7 +4203,7 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no
temperature = 260 temperature = 260
filament_retract_length = nil filament_retract_length = nil
filament_retract_lift = 0.4 filament_retract_lift = 0.4
compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
[filament:ColorFabb XT-CF20 @PG] [filament:ColorFabb XT-CF20 @PG]
inherits = ColorFabb XT-CF20; *PETPG*; *04PLUSPG* inherits = ColorFabb XT-CF20; *PETPG*; *04PLUSPG*
@ -4317,7 +4324,7 @@ filament_colour = #804040
filament_max_volumetric_speed = 6 filament_max_volumetric_speed = 6
first_layer_temperature = 260 first_layer_temperature = 260
temperature = 260 temperature = 260
compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
[filament:Kimya ABS Carbon @PG] [filament:Kimya ABS Carbon @PG]
inherits = Kimya ABS Carbon; *ABSPG*; *04PLUSPG* inherits = Kimya ABS Carbon; *ABSPG*; *04PLUSPG*
@ -4577,7 +4584,7 @@ filament_type = PC
filament_colour = #DEE0E6 filament_colour = #DEE0E6
filament_max_volumetric_speed = 8 filament_max_volumetric_speed = 8
filament_retract_lift = 0.2 filament_retract_lift = 0.2
compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
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.04{else}0.07{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 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" 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.04{else}0.07{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 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0"
[filament:Prusament PC Blend @PG] [filament:Prusament PC Blend @PG]
@ -4609,12 +4616,12 @@ inherits = Prusament PC Blend
first_layer_bed_temperature = 105 first_layer_bed_temperature = 105
bed_temperature = 110 bed_temperature = 110
disable_fan_first_layers = 6 disable_fan_first_layers = 6
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes=~/.*PRINTER_MODEL_MK(2|2.5).*/ and ! single_extruder_multi_material compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes=~/.*PRINTER_MODEL_MK(2|2.5).*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
[filament:Prusament PC Blend Carbon Fiber] [filament:Prusament PC Blend Carbon Fiber]
inherits = Prusament PC Blend inherits = Prusament PC Blend
filament_cost = 90.73 filament_cost = 90.73
filament_density = 1.16 filament_density = 1.22
extrusion_multiplier = 1.04 extrusion_multiplier = 1.04
first_layer_temperature = 285 first_layer_temperature = 285
temperature = 285 temperature = 285
@ -4623,7 +4630,7 @@ fan_below_layer_time = 10
filament_colour = #BBBBBB filament_colour = #BBBBBB
filament_retract_length = nil filament_retract_length = nil
filament_retract_lift = nil filament_retract_lift = nil
compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
[filament:Prusament PC Blend Carbon Fiber @PG] [filament:Prusament PC Blend Carbon Fiber @PG]
inherits = Prusament PC Blend Carbon Fiber; *PCPG* inherits = Prusament PC Blend Carbon Fiber; *PCPG*
@ -4667,7 +4674,7 @@ temperature = 285
first_layer_bed_temperature = 90 first_layer_bed_temperature = 90
bed_temperature = 115 bed_temperature = 115
fan_below_layer_time = 10 fan_below_layer_time = 10
compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
[filament:Prusament PA11 Carbon Fiber @PG] [filament:Prusament PA11 Carbon Fiber @PG]
inherits = Prusament PA11 Carbon Fiber; *PCPG* inherits = Prusament PA11 Carbon Fiber; *PCPG*
@ -4786,7 +4793,7 @@ inherits = *ABSC*
filament_vendor = Generic filament_vendor = Generic
filament_cost = 27.82 filament_cost = 27.82
filament_density = 1.04 filament_density = 1.04
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
[filament:Generic ABS @PG] [filament:Generic ABS @PG]
inherits = Generic ABS; *ABSPG* inherits = Generic ABS; *ABSPG*
@ -4923,7 +4930,7 @@ renamed_from = "Generic PET"
filament_vendor = Generic filament_vendor = Generic
filament_cost = 27.82 filament_cost = 27.82
filament_density = 1.27 filament_density = 1.27
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
[filament:Generic PETG @PG] [filament:Generic PETG @PG]
inherits = Generic PETG; *PETPG* inherits = Generic PETG; *PETPG*
@ -5393,7 +5400,7 @@ filament_retract_lift = 0.4
filament_max_volumetric_speed = 4 filament_max_volumetric_speed = 4
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.04{else}0.08{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 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" 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.04{else}0.08{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 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0"
filament_spool_weight = 0 filament_spool_weight = 0
compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and printer_model!="MK2SMM" and ! single_extruder_multi_material compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and printer_model!="MK2SMM" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
[filament:addnorth Adura X @PG] [filament:addnorth Adura X @PG]
inherits = addnorth Adura X; *PETPG* inherits = addnorth Adura X; *PETPG*
@ -5647,7 +5654,7 @@ filament_retract_length = 1.4
filament_max_volumetric_speed = 5 filament_max_volumetric_speed = 5
filament_spool_weight = 0 filament_spool_weight = 0
filament_notes = "Please use a nozzle that is resistant to abrasive filaments, like hardened steel." filament_notes = "Please use a nozzle that is resistant to abrasive filaments, like hardened steel."
compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and printer_model!="MK2SMM" and ! single_extruder_multi_material compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and printer_model!="MK2SMM" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
[filament:addnorth Rigid X @PG] [filament:addnorth Rigid X @PG]
inherits = addnorth Rigid X; *PETPG*; *04PLUSPG* inherits = addnorth Rigid X; *PETPG*; *04PLUSPG*
@ -5693,7 +5700,7 @@ slowdown_below_layer_time = 15
min_print_speed = 20 min_print_speed = 20
filament_spool_weight = 0 filament_spool_weight = 0
filament_retract_length = 1 filament_retract_length = 1
compatible_printers_condition = printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material compatible_printers_condition = printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
[filament:addnorth Textura @PG] [filament:addnorth Textura @PG]
inherits = addnorth Textura; *PLAPG* inherits = addnorth Textura; *PLAPG*
@ -5730,7 +5737,7 @@ disable_fan_first_layers = 3
fan_below_layer_time = 60 fan_below_layer_time = 60
slowdown_below_layer_time = 15 slowdown_below_layer_time = 15
bridge_fan_speed = 20 bridge_fan_speed = 20
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
[filament:Filamentworld ABS @PG] [filament:Filamentworld ABS @PG]
inherits = Filamentworld ABS; *ABSPG* inherits = Filamentworld ABS; *ABSPG*
@ -5827,7 +5834,7 @@ filament_vendor = Filament PM
filament_cost = 27.82 filament_cost = 27.82
filament_density = 1.27 filament_density = 1.27
filament_spool_weight = 230 filament_spool_weight = 230
compatible_printers_condition = nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material compatible_printers_condition = nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
[filament:Filament PM PETG @PG] [filament:Filament PM PETG @PG]
inherits = Filament PM PETG; *PETPG* inherits = Filament PM PETG; *PETPG*
@ -5843,7 +5850,7 @@ inherits = *PLA*
filament_vendor = Generic filament_vendor = Generic
filament_cost = 25.4 filament_cost = 25.4
filament_density = 1.24 filament_density = 1.24
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
[filament:Generic PLA @PG] [filament:Generic PLA @PG]
inherits = Generic PLA; *PLAPG* inherits = Generic PLA; *PLAPG*
@ -6092,6 +6099,220 @@ inherits = Spectrum PLA; *PLA06PG*
[filament:Spectrum PLA @PG 0.8] [filament:Spectrum PLA @PG 0.8]
inherits = Spectrum PLA; *PLA08PG* inherits = Spectrum PLA; *PLA08PG*
[filament:Spectrum PETG Matt]
inherits = *PET*
filament_vendor = Spectrum
bed_temperature = 90
bridge_fan_speed = 50
extrusion_multiplier = 1.1
disable_fan_first_layers = 1
full_fan_speed_layer = 1
fan_always_on = 1
fan_below_layer_time = 20
filament_colour = #FF8000
filament_max_volumetric_speed = 8
filament_type = PETG
first_layer_bed_temperature = 85
first_layer_temperature = 230
max_fan_speed = 100
min_fan_speed = 30
temperature = 240
filament_density = 1.35
[filament:Spectrum PETG Matt @PG]
inherits = Spectrum PETG Matt; *PETPG*
[filament:Spectrum PETG Matt @PG 0.6]
inherits = Spectrum PETG Matt @PG; *PET06PG*
[filament:Spectrum PETG Matt @PG 0.8]
inherits = Spectrum PETG Matt @PG; *PET08PG*
[filament:Spectrum PETG Matt @MINI]
inherits = Spectrum PETG Matt; *PETMINI*
[filament:Spectrum PETG HT100]
inherits = *PET*
filament_vendor = Spectrum
bed_temperature = 105
bridge_fan_speed = 50
extrusion_multiplier = 1
disable_fan_first_layers = 1
full_fan_speed_layer = 1
fan_always_on = 1
fan_below_layer_time = 20
filament_colour = #FF8000
filament_max_volumetric_speed = 8
filament_type = PETG
first_layer_bed_temperature = 105
first_layer_temperature = 250
max_fan_speed = 100
min_fan_speed = 30
temperature = 250
filament_density = 1.24
[filament:Spectrum PETG HT100 @PG]
inherits = Spectrum PETG HT100; *PETPG*
[filament:Spectrum PETG HT100 @PG 0.6]
inherits = Spectrum PETG HT100 @PG; *PET06PG*
[filament:Spectrum PETG HT100 @PG 0.8]
inherits = Spectrum PETG HT100 @PG; *PET08PG*
[filament:Spectrum PETG HT100 @MINI]
inherits = Spectrum PETG HT100; *PETMINI*
bed_temperature = 100
first_layer_bed_temperature = 100
[filament:Spectrum GreenyHT]
inherits = *PLA*
filament_vendor = Spectrum
first_layer_temperature = 205
first_layer_bed_temperature = 45
temperature = 205
bed_temperature = 45
bridge_fan_speed = 50
extrusion_multiplier = 1.0
disable_fan_first_layers = 1
full_fan_speed_layer = 1
fan_always_on = 1
fan_below_layer_time = 20
filament_colour = #FF8000
filament_max_volumetric_speed = 8
filament_type = PLA
max_fan_speed = 100
min_fan_speed = 30
filament_density = 1.54
[filament:Spectrum GreenyHT @PG]
inherits = Spectrum GreenyHT; *PLAPG*
[filament:Spectrum GreenyHT @PG 0.6]
inherits = Spectrum GreenyHT @PG; *PLA06PG*
[filament:Spectrum GreenyHT @PG 0.8]
inherits = Spectrum GreenyHT @PG; *PLA08PG*
[filament:Spectrum ASA 275]
inherits = *ABSC*
filament_vendor = Spectrum
first_layer_temperature = 237
first_layer_bed_temperature = 80
temperature = 237
bed_temperature = 80
extrusion_multiplier = 0.98
filament_type = ASA
filament_density = 1.24
[filament:Spectrum ASA 275 @PG]
inherits = Spectrum ASA 275; *ABSPG*
compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material
[filament:Spectrum ASA 275 @PG 0.6]
inherits = Spectrum ASA 275 @PG; *ABS06PG*
compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material
[filament:Spectrum ASA 275 @PG 0.8]
inherits = Spectrum ASA 275 @PG; *ABS08PG*
compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material
[filament:Spectrum ASA 275 @MINI]
inherits = Spectrum ASA 275; *ABSMINI*
temperature = 235
bed_temperature = 80
extrusion_multiplier = 1
[filament:Spectrum ASA Kevlar]
inherits = *ABSC*
filament_vendor = Spectrum
temperature = 250
bed_temperature = 105
extrusion_multiplier = 1.04
filament_type = ASA
filament_density = 1.24
compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
[filament:Spectrum ASA Kevlar @PG]
inherits = Spectrum ASA Kevlar; *ABSPG*
compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=="XL"
[filament:Spectrum ASA Kevlar @PG 0.6]
inherits = Spectrum ASA Kevlar @PG; *ABS06PG*
[filament:Spectrum ASA Kevlar @PG 0.8]
inherits = Spectrum ASA Kevlar @PG; *ABS08PG*
[filament:Spectrum ASA Kevlar @MK4]
inherits = Spectrum ASA Kevlar; *ABSMK4*
compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=="MK4"
[filament:Spectrum ASA Kevlar @MK4 0.6]
inherits = Spectrum ASA Kevlar @MK4; *ABS06MK4*
[filament:Spectrum ASA Kevlar @MK4 0.8]
inherits = Spectrum ASA Kevlar @MK4; *ABS08MK4*
[filament:Spectrum ASA Kevlar @MINI]
inherits = Spectrum ASA Kevlar; *ABSMINI*
temperature = 250
bed_temperature = 100
extrusion_multiplier = 1.03
compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model=="MINI"
[filament:Spectrum Tough PLA]
inherits = *PLA*
filament_vendor = Spectrum
temperature = 235
bed_temperature = 45
extrusion_multiplier = 0.95
filament_type = PLA Tough
filament_density = 1.24
[filament:Spectrum Tough PLA @PG]
inherits = Spectrum Tough PLA; *PLAPG*
[filament:Spectrum Tough PLA @PG 0.6]
inherits = Spectrum Tough PLA @PG; *PLA06PG*
[filament:Spectrum Tough PLA @PG 0.8]
inherits = Spectrum Tough PLA @PG; *PLA08PG*
[filament:Spectrum PLA PRO]
inherits = *PLA*
filament_vendor = Spectrum
filament_type = PLA
filament_density = 1.22
[filament:Spectrum PLA PRO @PG]
inherits = Spectrum PLA PRO; *PLAPG*
[filament:Spectrum PLA PRO @PG 0.6]
inherits = Spectrum PLA PRO @PG; *PLA06PG*
[filament:Spectrum PLA PRO @PG 0.8]
inherits = Spectrum PLA PRO @PG; *PLA08PG*
[filament:Spectrum PCTG]
inherits = *PET*
filament_vendor = Spectrum
filament_type = PCTG
temperature = 240
bed_temperature = 90
filament_density = 1.27
[filament:Spectrum PCTG @PG]
inherits = Spectrum PCTG; *PETPG*
[filament:Spectrum PCTG @PG 0.6]
inherits = Spectrum PCTG @PG; *PET06PG*
[filament:Spectrum PCTG @PG 0.8]
inherits = Spectrum PCTG @PG; *PET08PG*
[filament:Spectrum PCTG @MINI]
inherits = Spectrum PCTG; *PETMINI*
[filament:Generic FLEX] [filament:Generic FLEX]
inherits = *FLEX* inherits = *FLEX*
filament_vendor = Generic filament_vendor = Generic
@ -6596,7 +6817,7 @@ extrusion_multiplier = 0.95
filament_density = 1.1 filament_density = 1.1
first_layer_bed_temperature = 105 first_layer_bed_temperature = 105
bed_temperature = 100 bed_temperature = 100
compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
[filament:Filatech FilaCarbon @PG] [filament:Filatech FilaCarbon @PG]
inherits = Filatech FilaCarbon; *ABSPG*; *04PLUSPG* inherits = Filatech FilaCarbon; *ABSPG*; *04PLUSPG*
@ -6698,7 +6919,7 @@ first_layer_temperature = 230
first_layer_bed_temperature = 100 first_layer_bed_temperature = 100
temperature = 225 temperature = 225
bed_temperature = 110 bed_temperature = 110
compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
[filament:Filatech HIPS @PG] [filament:Filatech HIPS @PG]
inherits = Filatech HIPS; *ABSPG* inherits = Filatech HIPS; *ABSPG*
@ -6755,7 +6976,7 @@ cooling = 0
bridge_fan_speed = 25 bridge_fan_speed = 25
filament_type = PA filament_type = PA
filament_max_volumetric_speed = 8 filament_max_volumetric_speed = 8
compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
[filament:Filatech PA @PG] [filament:Filatech PA @PG]
inherits = Filatech PA; *ABSPG* inherits = Filatech PA; *ABSPG*
@ -7235,7 +7456,7 @@ min_fan_speed = 20
max_fan_speed = 20 max_fan_speed = 20
bridge_fan_speed = 30 bridge_fan_speed = 30
disable_fan_first_layers = 4 disable_fan_first_layers = 4
compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
filament_notes = "Material Description\nUltrafuse® PC/ABS FR Black is a V-0 flame retardant blend of Polycarbonate and ABS two of the most used thermoplastics for engineering & electrical applications. The combination of these two materials results in a premium material with a mix of the excellent mechanical properties of PC and the comparably low printing temperature of ABS. Combined with a halogen free flame retardant, parts printed with Ultrafuse® PC/ABS FR Black feature great tensile and impact strength, higher thermal resistance than ABS and can fulfill the requirements of the UL94 V-0 standard.\n\nPrinting Recommendations:\nApply Magigoo PC to a clean build plate to improve adhesion." filament_notes = "Material Description\nUltrafuse® PC/ABS FR Black is a V-0 flame retardant blend of Polycarbonate and ABS two of the most used thermoplastics for engineering & electrical applications. The combination of these two materials results in a premium material with a mix of the excellent mechanical properties of PC and the comparably low printing temperature of ABS. Combined with a halogen free flame retardant, parts printed with Ultrafuse® PC/ABS FR Black feature great tensile and impact strength, higher thermal resistance than ABS and can fulfill the requirements of the UL94 V-0 standard.\n\nPrinting Recommendations:\nApply Magigoo PC to a clean build plate to improve adhesion."
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.04{else}0.07{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 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" 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.04{else}0.07{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 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0"
@ -7670,7 +7891,7 @@ filament_vendor = Made for Prusa
filament_cost = 27.82 filament_cost = 27.82
filament_density = 1.08 filament_density = 1.08
filament_spool_weight = 230 filament_spool_weight = 230
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
[filament:Prusa ABS @PG] [filament:Prusa ABS @PG]
inherits = Prusa ABS; *ABSPG* inherits = Prusa ABS; *ABSPG*
@ -7881,7 +8102,7 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no
[filament:Prusament PC Blend Carbon Fiber @MMU2] [filament:Prusament PC Blend Carbon Fiber @MMU2]
inherits = Prusament PC Blend @MMU2 inherits = Prusament PC Blend @MMU2
filament_cost = 90.73 filament_cost = 90.73
filament_density = 1.16 filament_density = 1.22
extrusion_multiplier = 1.04 extrusion_multiplier = 1.04
fan_below_layer_time = 10 fan_below_layer_time = 10
first_layer_temperature = 280 first_layer_temperature = 280
@ -7925,7 +8146,7 @@ max_fan_speed = 20
min_fan_speed = 20 min_fan_speed = 20
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.01{elsif nozzle_diameter[0]==0.6}0.03{else}0.04{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 K12{elsif nozzle_diameter[0]==0.8};{else}M900 K20{endif} ; Filament gcode LA 1.0" 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.01{elsif nozzle_diameter[0]==0.6}0.03{else}0.04{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 K12{elsif nozzle_diameter[0]==0.8};{else}M900 K20{endif} ; Filament gcode LA 1.0"
temperature = 220 temperature = 220
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
[filament:Generic HIPS] [filament:Generic HIPS]
inherits = *ABS* inherits = *ABS*
@ -7945,7 +8166,7 @@ max_fan_speed = 20
min_fan_speed = 20 min_fan_speed = 20
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.01{elsif nozzle_diameter[0]==0.6}0.03{else}0.04{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 K12{elsif nozzle_diameter[0]==0.8};{else}M900 K20{endif} ; Filament gcode LA 1.0" 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.01{elsif nozzle_diameter[0]==0.6}0.03{else}0.04{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 K12{elsif nozzle_diameter[0]==0.8};{else}M900 K20{endif} ; Filament gcode LA 1.0"
temperature = 230 temperature = 230
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
[filament:Generic HIPS @PG] [filament:Generic HIPS @PG]
inherits = Generic HIPS; *ABSPG* inherits = Generic HIPS; *ABSPG*
@ -7973,7 +8194,7 @@ filament_vendor = Made for Prusa
filament_cost = 27.82 filament_cost = 27.82
filament_density = 1.27 filament_density = 1.27
filament_spool_weight = 230 filament_spool_weight = 230
compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
[filament:Prusa PETG @PG] [filament:Prusa PETG @PG]
inherits = Prusa PETG; *PETPG* inherits = Prusa PETG; *PETPG*
@ -8239,7 +8460,7 @@ filament_vendor = Made for Prusa
filament_cost = 27.82 filament_cost = 27.82
filament_density = 1.24 filament_density = 1.24
filament_spool_weight = 230 filament_spool_weight = 230
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
[filament:Prusa PLA @PG] [filament:Prusa PLA @PG]
inherits = Prusa PLA; *PLAPG* inherits = Prusa PLA; *PLAPG*
@ -9350,7 +9571,7 @@ filament_cost = 36.29
filament_density = 1.24 filament_density = 1.24
filament_spool_weight = 201 filament_spool_weight = 201
filament_notes = "Affordable filament for everyday printing in premium quality manufactured in-house by Josef Prusa" filament_notes = "Affordable filament for everyday printing in premium quality manufactured in-house by Josef Prusa"
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
[filament:Prusament PLA @PG] [filament:Prusament PLA @PG]
inherits = Prusament PLA; *PLAPG* inherits = Prusament PLA; *PLAPG*
@ -9377,7 +9598,7 @@ filament_max_volumetric_speed = 8
filament_type = PVB filament_type = PVB
filament_soluble = 1 filament_soluble = 1
filament_colour = #FFFF6F filament_colour = #FFFF6F
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
slowdown_below_layer_time = 20 slowdown_below_layer_time = 20
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.08{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 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" 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.08{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 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0"
@ -9557,7 +9778,7 @@ temperature = 260
max_fan_speed = 0 max_fan_speed = 0
min_fan_speed = 0 min_fan_speed = 0
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.04{else}0.08{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 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" 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.04{else}0.08{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 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0"
compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
[filament:Taulman Bridge @PG] [filament:Taulman Bridge @PG]
inherits = Taulman Bridge; *ABSPG* inherits = Taulman Bridge; *ABSPG*
@ -9779,7 +10000,7 @@ temperature = 285
first_layer_bed_temperature = 90 first_layer_bed_temperature = 90
bed_temperature = 90 bed_temperature = 90
fan_below_layer_time = 10 fan_below_layer_time = 10
compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
max_fan_speed = 15 max_fan_speed = 15
min_fan_speed = 15 min_fan_speed = 15
filament_type = PA filament_type = PA
@ -10009,7 +10230,7 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no
temperature = 235 temperature = 235
filament_wipe = 0 filament_wipe = 0
filament_retract_lift = 0 filament_retract_lift = 0
compatible_printers_condition = nozzle_diameter[0]>=0.35 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material compatible_printers_condition = nozzle_diameter[0]>=0.35 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
[filament:FormFutura Centaur PP @PG] [filament:FormFutura Centaur PP @PG]
inherits = FormFutura Centaur PP; *PETPG* inherits = FormFutura Centaur PP; *PETPG*
@ -10468,7 +10689,7 @@ compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]!=0.
[filament:Prusament PC Blend Carbon Fiber @MINI] [filament:Prusament PC Blend Carbon Fiber @MINI]
inherits = Prusament PC Blend @MINI inherits = Prusament PC Blend @MINI
filament_cost = 90.73 filament_cost = 90.73
filament_density = 1.16 filament_density = 1.22
extrusion_multiplier = 1.04 extrusion_multiplier = 1.04
first_layer_temperature = 280 first_layer_temperature = 280
temperature = 280 temperature = 280

View File

@ -1,3 +1,6 @@
min_slic3r_version = 2.6.0-alpha6
1.0.3 Added Voron Switchwire.
1.0.2 Updated g-code flavor and travel accelerations.
min_slic3r_version = 2.4.2 min_slic3r_version = 2.4.2
1.0.1 Added 350mm Voron v1 variant. Updated max print heights. Removed redundant v1 volcano nozzle variants. 1.0.1 Added 350mm Voron v1 variant. Updated max print heights. Removed redundant v1 volcano nozzle variants.
min_slic3r_version = 2.4.0-beta0 min_slic3r_version = 2.4.0-beta0

View File

@ -7,7 +7,7 @@
name = Voron name = Voron
# Configuration version of this file. Config file will only be installed, if the config_version differs. # Configuration version of this file. Config file will only be installed, if the config_version differs.
# This means, the server may force the PrusaSlicer configuration to be downgraded. # This means, the server may force the PrusaSlicer configuration to be downgraded.
config_version = 1.0.1 config_version = 1.0.3
# Where to get the updates from? # Where to get the updates from?
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Voron/ config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Voron/
@ -106,6 +106,28 @@ bed_model = printbed-v0-120.stl
bed_texture = bedtexture-v0-120.png bed_texture = bedtexture-v0-120.png
default_materials = Basic PLA @VORON; Basic PLA VOLCANO @VORON; Basic PET @VORON; Basic PET VOLCANO @VORON; Basic ABS @VORON; Basic ABS VOLCANO @VORON default_materials = Basic PLA @VORON; Basic PLA VOLCANO @VORON; Basic PET @VORON; Basic PET VOLCANO @VORON; Basic ABS @VORON; Basic ABS VOLCANO @VORON
[printer_model:Voron_SW_afterburner]
name = Voron Switchwire
variants = 0.4; 0.25; 0.3; 0.5; 0.6; 0.8; volcano 0.6; volcano 0.8; volcano 1.0; volcano 1.2
technology = FFF
family = Voron Switchwire Afterburner
bed_model = printbed-SW-MK52.stl
bed_texture = bedtexture-SW-250x210.png
bed_with_grid = 1
default_materials = Basic PLA @VORON; Basic PLA VOLCANO @VORON; Basic PET @VORON; Basic PET VOLCANO @VORON; Basic ABS @VORON; Basic ABS VOLCANO @VORON
thumbnail = Voron_SW_thumbnail.png
[printer_model:Voron_SW]
name = Voron Switchwire
variants = 0.4; 0.25; 0.3; 0.5; 0.6; 0.8; volcano 0.6; volcano 0.8; volcano 1.0; volcano 1.2
technology = FFF
family = Voron Switchwire Mobius
bed_model = printbed-SW-MK52.stl
bed_texture = bedtexture-SW-250x210.png
bed_with_grid = 1
default_materials = Basic PLA @VORON; Basic PLA VOLCANO @VORON; Basic PET @VORON; Basic PET VOLCANO @VORON; Basic ABS @VORON; Basic ABS VOLCANO @VORON
thumbnail = Voron_SW_thumbnail.png
# All presets starting with asterisk, for example *common*, are intermediate and they will # All presets starting with asterisk, for example *common*, are intermediate and they will
# not make it into the user interface # not make it into the user interface
@ -183,7 +205,8 @@ deretract_speed = 25
end_gcode = print_end ;end script from macro end_gcode = print_end ;end script from macro
extruder_colour = #FFE3CA extruder_colour = #FFE3CA
extruder_offset = 0x0 extruder_offset = 0x0
gcode_flavor = marlin gcode_flavor = klipper
autoemit_temperature_commands = 1
layer_gcode = ;AFTER_LAYER_CHANGE\n;[layer_z] layer_gcode = ;AFTER_LAYER_CHANGE\n;[layer_z]
machine_max_acceleration_e = 10000 machine_max_acceleration_e = 10000
machine_max_acceleration_extruding = 1500 machine_max_acceleration_extruding = 1500
@ -309,6 +332,18 @@ max_print_height = 120
printer_model = Voron_v0_120 printer_model = Voron_v0_120
printer_notes = Unoffical profile.\nPRINTER_HAS_BOWDEN\nE3DV6 printer_notes = Unoffical profile.\nPRINTER_HAS_BOWDEN\nE3DV6
[printer:*Voron_Switchwire*]
inherits = *common*
bed_shape = 0x0,250x0,250x210,0x210
max_print_height = 240
printer_model = Voron_SW
printer_notes = PRINTER_HAS_BOWDEN\nSTU\nE3DV6
[printer:*Voron_Switchwire_afterburner*]
inherits = *Voron_Switchwire*; *afterburner*
printer_model = Voron_SW_afterburner
printer_notes = STU\nE3DV6
[printer:Voron_v2_250 0.25 nozzle] [printer:Voron_v2_250 0.25 nozzle]
inherits = *Voron_v2_250*; *0.25nozzle* inherits = *Voron_v2_250*; *0.25nozzle*
@ -657,6 +692,89 @@ printer_variant = volcano 1.2
printer_notes = Unoffical profile.\nPRINTER_HAS_BOWDEN\nVOLCANO printer_notes = Unoffical profile.\nPRINTER_HAS_BOWDEN\nVOLCANO
default_filament_profile = Basic PLA VOLCANO @VORON default_filament_profile = Basic PLA VOLCANO @VORON
[printer:Voron_Switchwire 0.25 nozzle]
inherits = *Voron_Switchwire*; *0.25nozzle*
[printer:Voron_Switchwire 0.3 nozzle]
inherits = *Voron_Switchwire*; *0.3nozzle*
[printer:Voron_Switchwire 0.4 nozzle]
inherits = *Voron_Switchwire*; *0.4nozzle*
[printer:Voron_Switchwire 0.5 nozzle]
inherits = *Voron_Switchwire*; *0.5nozzle*
[printer:Voron_Switchwire 0.6 nozzle]
inherits = *Voron_Switchwire*; *0.6nozzle*
[printer:Voron_Switchwire 0.8 nozzle]
inherits = *Voron_Switchwire*; *0.8nozzle*
[printer:Voron_Switchwire 0.6 volcano]
inherits = *Voron_Switchwire*; *0.6nozzle*; *volcano*
printer_variant = volcano 0.6
printer_notes = PRINTER_HAS_BOWDEN\nVOLCANO
default_filament_profile = Basic PLA VOLCANO @VORON
[printer:Voron_Switchwire 0.8 volcano]
inherits = *Voron_Switchwire*; *0.8nozzle*; *volcano*
printer_variant = volcano 0.8
printer_notes = PRINTER_HAS_BOWDEN\nVOLCANO
default_filament_profile = Basic PLA VOLCANO @VORON
[printer:Voron_Switchwire 1.0 volcano]
inherits = *Voron_Switchwire*; *1.0nozzle*; *volcano*
printer_variant = volcano 1.0
printer_notes = PRINTER_HAS_BOWDEN\nVOLCANO
default_filament_profile = Basic PLA VOLCANO @VORON
[printer:Voron_Switchwire 1.2 volcano]
inherits = *Voron_Switchwire*; *1.2nozzle*; *volcano*
printer_variant = volcano 1.2
printer_notes = PRINTER_HAS_BOWDEN\nVOLCANO
default_filament_profile = Basic PLA VOLCANO @VORON
[printer:Voron_Switchwire_afterburner 0.25 nozzle]
inherits = *Voron_Switchwire_afterburner*; *0.25nozzle*
[printer:Voron_Switchwire_afterburner 0.3 nozzle]
inherits = *Voron_Switchwire_afterburner*; *0.3nozzle*
[printer:Voron_Switchwire_afterburner 0.4 nozzle]
inherits = *Voron_Switchwire_afterburner*; *0.4nozzle*
[printer:Voron_Switchwire_afterburner 0.5 nozzle]
inherits = *Voron_Switchwire_afterburner*; *0.5nozzle*
[printer:Voron_Switchwire_afterburner 0.6 nozzle]
inherits = *Voron_Switchwire_afterburner*; *0.6nozzle*
[printer:Voron_Switchwire_afterburner 0.8 nozzle]
inherits = *Voron_Switchwire_afterburner*; *0.8nozzle*
[printer:Voron_Switchwire_afterburner volcano 0.6 nozzle]
inherits = *Voron_Switchwire_afterburner*; *0.6nozzle*; *volcano_afterburner*
printer_variant = volcano 0.6
printer_notes = VOLCANO
default_filament_profile = Basic PLA VOLCANO @VORON
[printer:Voron_Switchwire_afterburner volcano 0.8 nozzle]
inherits = *Voron_Switchwire_afterburner*; *0.8nozzle*; *volcano_afterburner*
printer_variant = volcano 0.8
printer_notes = VOLCANO
default_filament_profile = Basic PLA VOLCANO @VORON
[printer:Voron_Switchwire_afterburner volcano 1.0 nozzle]
inherits = *Voron_Switchwire_afterburner*; *1.0nozzle*; *volcano_afterburner*
printer_variant = volcano 1.0
printer_notes = VOLCANO
default_filament_profile = Basic PLA VOLCANO @VORON
[printer:Voron_Switchwire_afterburner volcano 1.2 nozzle]
inherits = *Voron_Switchwire_afterburner*; *1.2nozzle*; *volcano_afterburner*
printer_variant = volcano 1.2
printer_notes = VOLCANO
default_filament_profile = Basic PLA VOLCANO @VORON
# Common print preset, mostly derived from MK2 single material with a 0.4mm nozzle. # Common print preset, mostly derived from MK2 single material with a 0.4mm nozzle.
# All other print presets will derive from the *common* print preset. # All other print presets will derive from the *common* print preset.
@ -672,7 +790,8 @@ brim_width = 0
clip_multipart_objects = 1 clip_multipart_objects = 1
compatible_printers = compatible_printers =
complete_objects = 0 complete_objects = 0
default_acceleration = 3000 default_acceleration = 2000
travel_acceleration = 3000
dont_support_bridges = 1 dont_support_bridges = 1
ensure_vertical_shell_thickness = 1 ensure_vertical_shell_thickness = 1
external_perimeters_first = 0 external_perimeters_first = 0
@ -1020,6 +1139,22 @@ compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diamete
inherits = *0.05mm*; *0.5nozzle*; *zero_toolhead* inherits = *0.05mm*; *0.5nozzle*; *zero_toolhead*
compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diameter[0]==0.5 compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diameter[0]==0.5
[print:0.05mm 0.25nozzle SW]
inherits = *0.05mm*; *0.25nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.25
[print:0.05mm 0.3nozzle SW]
inherits = *0.05mm*; *0.3nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.3
[print:0.05mm 0.4nozzle SW]
inherits = *0.05mm*; *0.4nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.4
[print:0.05mm 0.5nozzle SW]
inherits = *0.05mm*; *0.5nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.5
[print:0.10mm 0.25nozzle V2] [print:0.10mm 0.25nozzle V2]
inherits = *0.10mm*; *0.25nozzle* inherits = *0.10mm*; *0.25nozzle*
compatible_printers_condition = printer_model=~/.*Voron_v2.*/ and nozzle_diameter[0]==0.25 compatible_printers_condition = printer_model=~/.*Voron_v2.*/ and nozzle_diameter[0]==0.25
@ -1092,6 +1227,30 @@ compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diamete
inherits = *0.10mm*; *0.8nozzle*; *zero_toolhead* inherits = *0.10mm*; *0.8nozzle*; *zero_toolhead*
compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diameter[0]==0.8 compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diameter[0]==0.8
[print:0.10mm 0.25nozzle SW]
inherits = *0.10mm*; *0.25nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.25
[print:0.10mm 0.3nozzle SW]
inherits = *0.10mm*; *0.3nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.3
[print:0.10mm 0.4nozzle SW]
inherits = *0.10mm*; *0.4nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.4
[print:0.10mm 0.5nozzle SW]
inherits = *0.10mm*; *0.5nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.5
[print:0.10mm 0.6nozzle SW]
inherits = *0.10mm*; *0.6nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.6
[print:0.10mm 0.8nozzle SW]
inherits = *0.10mm*; *0.8nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.8
[print:0.15mm 0.25nozzle V2] [print:0.15mm 0.25nozzle V2]
inherits = *0.15mm*; *0.25nozzle* inherits = *0.15mm*; *0.25nozzle*
compatible_printers_condition = printer_model=~/.*Voron_v2.*/ and nozzle_diameter[0]==0.25 compatible_printers_condition = printer_model=~/.*Voron_v2.*/ and nozzle_diameter[0]==0.25
@ -1180,6 +1339,37 @@ compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diamete
inherits = *0.15mm*; *1.2nozzle*; *zero_toolhead* inherits = *0.15mm*; *1.2nozzle*; *zero_toolhead*
compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diameter[0]==1.2 compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diameter[0]==1.2
[print:0.15mm 0.25nozzle SW]
inherits = *0.15mm*; *0.25nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.25
[print:0.15mm 0.3nozzle SW]
inherits = *0.15mm*; *0.3nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.3
[print:0.15mm 0.4nozzle SW]
inherits = *0.15mm*; *0.4nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.4
[print:0.15mm 0.5nozzle SW]
inherits = *0.15mm*; *0.5nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.5
[print:0.15mm 0.6nozzle SW]
inherits = *0.15mm*; *0.6nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.6
[print:0.15mm 0.8nozzle SW]
inherits = *0.15mm*; *0.8nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.8
[print:0.15mm 1.0nozzle SW]
inherits = *0.15mm*; *1.0nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==1.0
[print:0.15mm 1.2nozzle SW]
inherits = *0.15mm*; *1.2nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==1.2
[print:0.2mm 0.3nozzle V2] [print:0.2mm 0.3nozzle V2]
inherits = *0.2mm*; *0.3nozzle* inherits = *0.2mm*; *0.3nozzle*
@ -1261,6 +1451,37 @@ compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diamete
inherits = *0.2mm*; *1.2nozzle*; *zero_toolhead* inherits = *0.2mm*; *1.2nozzle*; *zero_toolhead*
compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diameter[0]==1.2 compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diameter[0]==1.2
[print:0.2mm 0.25nozzle SW]
inherits = *0.2mm*; *0.25nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.25
[print:0.2mm 0.3nozzle SW]
inherits = *0.2mm*; *0.3nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.3
[print:0.2mm 0.4nozzle SW]
inherits = *0.2mm*; *0.4nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.4
[print:0.2mm 0.5nozzle SW]
inherits = *0.2mm*; *0.5nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.5
[print:0.2mm 0.6nozzle SW]
inherits = *0.2mm*; *0.6nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.6
[print:0.2mm 0.8nozzle SW]
inherits = *0.2mm*; *0.8nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.8
[print:0.2mm 1.0nozzle SW]
inherits = *0.2mm*; *1.0nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==1.0
[print:0.2mm 1.2nozzle SW]
inherits = *0.2mm*; *1.2nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==1.2
[print:0.3mm 0.4nozzle V2] [print:0.3mm 0.4nozzle V2]
inherits = *0.3mm*; *0.4nozzle* inherits = *0.3mm*; *0.4nozzle*
@ -1334,6 +1555,37 @@ compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diamete
inherits = *0.3mm*; *1.2nozzle*; *zero_toolhead* inherits = *0.3mm*; *1.2nozzle*; *zero_toolhead*
compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diameter[0]==1.2 compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diameter[0]==1.2
[print:0.3mm 0.25nozzle SW]
inherits = *0.3mm*; *0.25nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.25
[print:0.3mm 0.3nozzle SW]
inherits = *0.3mm*; *0.3nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.3
[print:0.3mm 0.4nozzle SW]
inherits = *0.3mm*; *0.4nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.4
[print:0.3mm 0.5nozzle SW]
inherits = *0.3mm*; *0.5nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.5
[print:0.3mm 0.6nozzle SW]
inherits = *0.3mm*; *0.6nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.6
[print:0.3mm 0.8nozzle SW]
inherits = *0.3mm*; *0.8nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.8
[print:0.3mm 1.0nozzle SW]
inherits = *0.3mm*; *1.0nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==1.0
[print:0.3mm 1.2nozzle SW]
inherits = *0.3mm*; *1.2nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==1.2
[print:0.4mm 0.6nozzle V2] [print:0.4mm 0.6nozzle V2]
inherits = *0.4mm*; *0.6nozzle* inherits = *0.4mm*; *0.6nozzle*
@ -1391,6 +1643,38 @@ compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diamete
inherits = *0.4mm*; *1.2nozzle*; *zero_toolhead* inherits = *0.4mm*; *1.2nozzle*; *zero_toolhead*
compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diameter[0]==1.2 compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diameter[0]==1.2
[print:0.4mm 0.25nozzle SW]
inherits = *0.4mm*; *0.25nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.25
[print:0.4mm 0.3nozzle SW]
inherits = *0.4mm*; *0.3nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.3
[print:0.4mm 0.4nozzle SW]
inherits = *0.4mm*; *0.4nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.4
[print:0.4mm 0.5nozzle SW]
inherits = *0.4mm*; *0.5nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.5
[print:0.4mm 0.6nozzle SW]
inherits = *0.4mm*; *0.6nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.6
[print:0.4mm 0.8nozzle SW]
inherits = *0.4mm*; *0.8nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.8
[print:0.4mm 1.0nozzle SW]
inherits = *0.4mm*; *1.0nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==1.0
[print:0.4mm 1.2nozzle SW]
inherits = *0.4mm*; *1.2nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==1.2
[print:0.6mm 0.8nozzle V2] [print:0.6mm 0.8nozzle V2]
inherits = *0.6mm*; *0.8nozzle* inherits = *0.6mm*; *0.8nozzle*
compatible_printers_condition = printer_model=~/.*Voron_v2.*/ and nozzle_diameter[0]==0.8 compatible_printers_condition = printer_model=~/.*Voron_v2.*/ and nozzle_diameter[0]==0.8
@ -1427,6 +1711,18 @@ compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diamete
inherits = *0.6mm*; *1.2nozzle*; *zero_toolhead* inherits = *0.6mm*; *1.2nozzle*; *zero_toolhead*
compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diameter[0]==1.2 compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diameter[0]==1.2
[print:0.6mm 0.8nozzle SW]
inherits = *0.6mm*; *0.6nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==0.8
[print:0.6mm 1.0nozzle SW]
inherits = *0.6mm*; *0.8nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==1.0
[print:0.6mm 1.2nozzle SW]
inherits = *0.6mm*; *0.8nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==1.2
[print:0.8mm 1.2nozzle V2] [print:0.8mm 1.2nozzle V2]
inherits = *0.8mm*; *1.2nozzle* inherits = *0.8mm*; *1.2nozzle*
compatible_printers_condition = printer_model=~/.*Voron_v2.*/ and nozzle_diameter[0]==1.2 compatible_printers_condition = printer_model=~/.*Voron_v2.*/ and nozzle_diameter[0]==1.2
@ -1439,6 +1735,10 @@ compatible_printers_condition = printer_model=~/.*Voron_v1.*/ and nozzle_diamete
inherits = *0.8mm*; *1.2nozzle*; *zero_toolhead* inherits = *0.8mm*; *1.2nozzle*; *zero_toolhead*
compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diameter[0]==1.2 compatible_printers_condition = printer_model=~/.*Voron_v0.*/ and nozzle_diameter[0]==1.2
[print:0.8mm 1.2nozzle SW]
inherits = *0.8mm*; *1.2nozzle*
compatible_printers_condition = printer_model=~/.*Voron_SW.*/ and nozzle_diameter[0]==1.2
[filament:*common*] [filament:*common*]
cooling = 1 cooling = 1

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 KiB

Binary file not shown.

7
src/ankerl/README.txt Normal file
View File

@ -0,0 +1,7 @@
THIS DIRECTORY CONTAINS PIECES OF THE
ankerl::unordered_dense::{map, set}
https://github.com/martinus/unordered_dense
unordered_dense 3.1.1 10782bfc651c2bb75b11bf90491f50da122e5432
SOURCE DISTRIBUTION.
THIS IS NOT THE COMPLETE unordered_dense DISTRIBUTION. ONLY FILES NEEDED FOR COMPILING PRUSASLICER WERE PUT INTO THE PRUSASLICER SOURCE DISTRIBUTION.

1584
src/ankerl/unordered_dense.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -8,3 +8,5 @@ add_library(clipper STATIC
clipper_z.cpp clipper_z.cpp
clipper_z.hpp clipper_z.hpp
) )
target_link_libraries(clipper TBB::tbb TBB::tbbmalloc)

View File

@ -784,7 +784,7 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed)
return false; return false;
// Allocate a new edge array. // Allocate a new edge array.
std::vector<TEdge> edges(highI + 1); Edges edges(highI + 1);
// Fill in the edge array. // Fill in the edge array.
bool result = AddPathInternal(pg, highI, PolyTyp, Closed, edges.data()); bool result = AddPathInternal(pg, highI, PolyTyp, Closed, edges.data());
if (result) if (result)
@ -1079,7 +1079,7 @@ Clipper::Clipper(int initOptions) :
void Clipper::Reset() void Clipper::Reset()
{ {
ClipperBase::Reset(); ClipperBase::Reset();
m_Scanbeam = std::priority_queue<cInt>(); m_Scanbeam = std::priority_queue<cInt, cInts>{};
m_Maxima.clear(); m_Maxima.clear();
m_ActiveEdges = 0; m_ActiveEdges = 0;
m_SortedEdges = 0; m_SortedEdges = 0;
@ -2226,8 +2226,8 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge)
if (!eLastHorz->NextInLML) if (!eLastHorz->NextInLML)
eMaxPair = GetMaximaPair(eLastHorz); eMaxPair = GetMaximaPair(eLastHorz);
std::vector<cInt>::const_iterator maxIt; cInts::const_iterator maxIt;
std::vector<cInt>::const_reverse_iterator maxRit; cInts::const_reverse_iterator maxRit;
if (!m_Maxima.empty()) if (!m_Maxima.empty())
{ {
//get the first maxima in range (X) ... //get the first maxima in range (X) ...
@ -3941,7 +3941,7 @@ void CleanPolygon(const Path& in_poly, Path& out_poly, double distance)
return; return;
} }
std::vector<OutPt> outPts(size); OutPts outPts(size);
for (size_t i = 0; i < size; ++i) for (size_t i = 0; i < size; ++i)
{ {
outPts[i].Pt = in_poly[i]; outPts[i].Pt = in_poly[i];

View File

@ -39,6 +39,8 @@
#include <Eigen/Geometry> #include <Eigen/Geometry>
#include <oneapi/tbb/scalable_allocator.h>
#define CLIPPER_VERSION "6.2.6" #define CLIPPER_VERSION "6.2.6"
//CLIPPERLIB_USE_XYZ: adds a Z member to IntPoint. Adds a minor cost to perfomance. //CLIPPERLIB_USE_XYZ: adds a Z member to IntPoint. Adds a minor cost to perfomance.
@ -112,8 +114,11 @@ using DoublePoint = Eigen::Matrix<double, 2, 1, Eigen::DontAlign>;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
typedef std::vector<IntPoint> Path; template<typename BaseType>
typedef std::vector<Path> Paths; using Allocator = tbb::scalable_allocator<BaseType>;
//using Allocator = std::allocator<BaseType>;
using Path = std::vector<IntPoint, Allocator<IntPoint>>;
using Paths = std::vector<Path, Allocator<Path>>;
inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;} inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;}
inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;} inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;}
@ -133,7 +138,7 @@ enum JoinType {jtSquare, jtRound, jtMiter};
enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound}; enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound};
class PolyNode; class PolyNode;
typedef std::vector< PolyNode* > PolyNodes; typedef std::vector<PolyNode*, Allocator<PolyNode*>> PolyNodes;
class PolyNode class PolyNode
{ {
@ -186,7 +191,7 @@ public:
private: private:
PolyTree(const PolyTree &src) = delete; PolyTree(const PolyTree &src) = delete;
PolyTree& operator=(const PolyTree &src) = delete; PolyTree& operator=(const PolyTree &src) = delete;
std::vector<PolyNode> AllNodes; std::vector<PolyNode, Allocator<PolyNode>> AllNodes;
friend class Clipper; //to access AllNodes friend class Clipper; //to access AllNodes
}; };
@ -277,6 +282,8 @@ enum EdgeSide { esLeft = 1, esRight = 2};
OutPt *Prev; OutPt *Prev;
}; };
using OutPts = std::vector<OutPt, Allocator<OutPt>>;
struct OutRec; struct OutRec;
struct Join { struct Join {
Join(OutPt *OutPt1, OutPt *OutPt2, IntPoint OffPt) : Join(OutPt *OutPt1, OutPt *OutPt2, IntPoint OffPt) :
@ -312,7 +319,7 @@ public:
if (num_paths == 1) if (num_paths == 1)
return AddPath(*paths_provider.begin(), PolyTyp, Closed); return AddPath(*paths_provider.begin(), PolyTyp, Closed);
std::vector<int> num_edges(num_paths, 0); std::vector<int, Allocator<int>> num_edges(num_paths, 0);
int num_edges_total = 0; int num_edges_total = 0;
size_t i = 0; size_t i = 0;
for (const Path &pg : paths_provider) { for (const Path &pg : paths_provider) {
@ -333,7 +340,7 @@ public:
return false; return false;
// Allocate a new edge array. // Allocate a new edge array.
std::vector<TEdge> edges(num_edges_total); std::vector<TEdge, Allocator<TEdge>> edges(num_edges_total);
// Fill in the edge array. // Fill in the edge array.
bool result = false; bool result = false;
TEdge *p_edge = edges.data(); TEdge *p_edge = edges.data();
@ -369,7 +376,7 @@ protected:
void AscendToMax(TEdge *&E, bool Appending, bool IsClosed); void AscendToMax(TEdge *&E, bool Appending, bool IsClosed);
// Local minima (Y, left edge, right edge) sorted by ascending Y. // Local minima (Y, left edge, right edge) sorted by ascending Y.
std::vector<LocalMinimum> m_MinimaList; std::vector<LocalMinimum, Allocator<LocalMinimum>> m_MinimaList;
#ifdef CLIPPERLIB_INT32 #ifdef CLIPPERLIB_INT32
static constexpr const bool m_UseFullRange = false; static constexpr const bool m_UseFullRange = false;
@ -380,7 +387,8 @@ protected:
#endif // CLIPPERLIB_INT32 #endif // CLIPPERLIB_INT32
// A vector of edges per each input path. // A vector of edges per each input path.
std::vector<std::vector<TEdge>> m_edges; using Edges = std::vector<TEdge, Allocator<TEdge>>;
std::vector<Edges, Allocator<Edges>> m_edges;
// Don't remove intermediate vertices of a collinear sequence of points. // Don't remove intermediate vertices of a collinear sequence of points.
bool m_PreserveCollinear; bool m_PreserveCollinear;
// Is any of the paths inserted by AddPath() or AddPaths() open? // Is any of the paths inserted by AddPath() or AddPaths() open?
@ -424,22 +432,23 @@ protected:
private: private:
// Output polygons. // Output polygons.
std::vector<OutRec*> m_PolyOuts; std::vector<OutRec*, Allocator<OutRec*>> m_PolyOuts;
// Output points, allocated by a continuous sets of m_OutPtsChunkSize. // Output points, allocated by a continuous sets of m_OutPtsChunkSize.
std::vector<OutPt*> m_OutPts; std::vector<OutPt*, Allocator<OutPt*>> m_OutPts;
// List of free output points, to be used before taking a point from m_OutPts or allocating a new chunk. // List of free output points, to be used before taking a point from m_OutPts or allocating a new chunk.
OutPt *m_OutPtsFree; OutPt *m_OutPtsFree;
size_t m_OutPtsChunkSize; size_t m_OutPtsChunkSize;
size_t m_OutPtsChunkLast; size_t m_OutPtsChunkLast;
std::vector<Join> m_Joins; std::vector<Join, Allocator<Join>> m_Joins;
std::vector<Join> m_GhostJoins; std::vector<Join, Allocator<Join>> m_GhostJoins;
std::vector<IntersectNode> m_IntersectList; std::vector<IntersectNode, Allocator<IntersectNode>> m_IntersectList;
ClipType m_ClipType; ClipType m_ClipType;
// A priority queue (a binary heap) of Y coordinates. // A priority queue (a binary heap) of Y coordinates.
std::priority_queue<cInt> m_Scanbeam; using cInts = std::vector<cInt, Allocator<cInt>>;
std::priority_queue<cInt, cInts> m_Scanbeam;
// Maxima are collected by ProcessEdgesAtTopOfScanbeam(), consumed by ProcessHorizontal(). // Maxima are collected by ProcessEdgesAtTopOfScanbeam(), consumed by ProcessHorizontal().
std::vector<cInt> m_Maxima; cInts m_Maxima;
TEdge *m_ActiveEdges; TEdge *m_ActiveEdges;
TEdge *m_SortedEdges; TEdge *m_SortedEdges;
PolyFillType m_ClipFillType; PolyFillType m_ClipFillType;
@ -530,7 +539,7 @@ private:
Paths m_destPolys; Paths m_destPolys;
Path m_srcPoly; Path m_srcPoly;
Path m_destPoly; Path m_destPoly;
std::vector<DoublePoint> m_normals; std::vector<DoublePoint, Allocator<DoublePoint>> m_normals;
double m_delta, m_sinA, m_sin, m_cos; double m_delta, m_sinA, m_sin, m_cos;
double m_miterLim, m_StepsPerRad; double m_miterLim, m_StepsPerRad;
// x: index of the lowest contour in m_polyNodes // x: index of the lowest contour in m_polyNodes

View File

@ -24,5 +24,5 @@ set(LIBNEST2D_SRCFILES
add_library(libnest2d STATIC ${LIBNEST2D_SRCFILES}) add_library(libnest2d STATIC ${LIBNEST2D_SRCFILES})
target_include_directories(libnest2d PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) target_include_directories(libnest2d PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include)
target_link_libraries(libnest2d PUBLIC NLopt::nlopt TBB::tbb Boost::boost libslic3r) target_link_libraries(libnest2d PUBLIC NLopt::nlopt TBB::tbb TBB::tbbmalloc Boost::boost libslic3r)
target_compile_definitions(libnest2d PUBLIC LIBNEST2D_THREADING_tbb LIBNEST2D_STATIC LIBNEST2D_OPTIMIZER_nlopt LIBNEST2D_GEOMETRIES_libslic3r) target_compile_definitions(libnest2d PUBLIC LIBNEST2D_THREADING_tbb LIBNEST2D_STATIC LIBNEST2D_OPTIMIZER_nlopt LIBNEST2D_GEOMETRIES_libslic3r)

View File

@ -339,15 +339,15 @@ public:
return {distance, nearest_line_index_out, nearest_point_out}; return {distance, nearest_line_index_out, nearest_point_out};
} }
template<bool SIGNED_DISTANCE> Floating distance_from_lines(const Vec<2, typename LineType::Scalar> &point) const template<bool SIGNED_DISTANCE> Floating distance_from_lines(const Vec<2, Scalar> &point) const
{ {
auto [dist, idx, np] = distance_from_lines_extra<SIGNED_DISTANCE>(point); auto [dist, idx, np] = distance_from_lines_extra<SIGNED_DISTANCE>(point);
return dist; return dist;
} }
std::vector<size_t> all_lines_in_radius(const Vec<2, typename LineType::Scalar> &point, Floating radius) std::vector<size_t> all_lines_in_radius(const Vec<2, Scalar> &point, Floating radius)
{ {
return all_lines_in_radius(this->lines, this->tree, point, radius * radius); return AABBTreeLines::all_lines_in_radius(this->lines, this->tree, point.template cast<Floating>(), radius * radius);
} }
template<bool sorted> std::vector<std::pair<Vec<2, Scalar>, size_t>> intersections_with_line(const LineType &line) const template<bool sorted> std::vector<std::pair<Vec<2, Scalar>, size_t>> intersections_with_line(const LineType &line) const

View File

@ -206,7 +206,7 @@ std::vector<WaveSeed> wave_seeds(
{ {
assert(tiny_expansion > 0); assert(tiny_expansion > 0);
if (src.empty()) if (src.empty() || boundary.empty())
return {}; return {};
using Intersection = ClipperZUtils::ClipperZIntersectionVisitor::Intersection; using Intersection = ClipperZUtils::ClipperZIntersectionVisitor::Intersection;

View File

@ -32,20 +32,20 @@ BeadingStrategyPtr BeadingStrategyFactory::makeStrategy(
) )
{ {
BeadingStrategyPtr ret = std::make_unique<DistributedBeadingStrategy>(preferred_bead_width_inner, preferred_transition_length, transitioning_angle, wall_split_middle_threshold, wall_add_middle_threshold, inward_distributed_center_wall_count); BeadingStrategyPtr ret = std::make_unique<DistributedBeadingStrategy>(preferred_bead_width_inner, preferred_transition_length, transitioning_angle, wall_split_middle_threshold, wall_add_middle_threshold, inward_distributed_center_wall_count);
BOOST_LOG_TRIVIAL(debug) << "Applying the Redistribute meta-strategy with outer-wall width = " << preferred_bead_width_outer << ", inner-wall width = " << preferred_bead_width_inner << "."; BOOST_LOG_TRIVIAL(trace) << "Applying the Redistribute meta-strategy with outer-wall width = " << preferred_bead_width_outer << ", inner-wall width = " << preferred_bead_width_inner << ".";
ret = std::make_unique<RedistributeBeadingStrategy>(preferred_bead_width_outer, minimum_variable_line_ratio, std::move(ret)); ret = std::make_unique<RedistributeBeadingStrategy>(preferred_bead_width_outer, minimum_variable_line_ratio, std::move(ret));
if (print_thin_walls) { if (print_thin_walls) {
BOOST_LOG_TRIVIAL(debug) << "Applying the Widening Beading meta-strategy with minimum input width " << min_feature_size << " and minimum output width " << min_bead_width << "."; BOOST_LOG_TRIVIAL(trace) << "Applying the Widening Beading meta-strategy with minimum input width " << min_feature_size << " and minimum output width " << min_bead_width << ".";
ret = std::make_unique<WideningBeadingStrategy>(std::move(ret), min_feature_size, min_bead_width); ret = std::make_unique<WideningBeadingStrategy>(std::move(ret), min_feature_size, min_bead_width);
} }
if (outer_wall_offset > 0) { if (outer_wall_offset > 0) {
BOOST_LOG_TRIVIAL(debug) << "Applying the OuterWallOffset meta-strategy with offset = " << outer_wall_offset << "."; BOOST_LOG_TRIVIAL(trace) << "Applying the OuterWallOffset meta-strategy with offset = " << outer_wall_offset << ".";
ret = std::make_unique<OuterWallInsetBeadingStrategy>(outer_wall_offset, std::move(ret)); ret = std::make_unique<OuterWallInsetBeadingStrategy>(outer_wall_offset, std::move(ret));
} }
//Apply the LimitedBeadingStrategy last, since that adds a 0-width marker wall which other beading strategies shouldn't touch. //Apply the LimitedBeadingStrategy last, since that adds a 0-width marker wall which other beading strategies shouldn't touch.
BOOST_LOG_TRIVIAL(debug) << "Applying the Limited Beading meta-strategy with maximum bead count = " << max_bead_count << "."; BOOST_LOG_TRIVIAL(trace) << "Applying the Limited Beading meta-strategy with maximum bead count = " << max_bead_count << ".";
ret = std::make_unique<LimitedBeadingStrategy>(max_bead_count, std::move(ret)); ret = std::make_unique<LimitedBeadingStrategy>(max_bead_count, std::move(ret));
return ret; return ret;
} }

View File

@ -5,7 +5,6 @@
#include <stack> #include <stack>
#include <functional> #include <functional>
#include <unordered_set>
#include <sstream> #include <sstream>
#include <queue> #include <queue>
#include <functional> #include <functional>
@ -181,7 +180,7 @@ void SkeletalTrapezoidation::transferEdge(Point from, Point to, vd_t::edge_type&
} }
else else
{ {
std::vector<Point> discretized = discretize(vd_edge, segments); Points discretized = discretize(vd_edge, segments);
assert(discretized.size() >= 2); assert(discretized.size() >= 2);
if(discretized.size() < 2) if(discretized.size() < 2)
{ {
@ -236,7 +235,7 @@ void SkeletalTrapezoidation::transferEdge(Point from, Point to, vd_t::edge_type&
} }
} }
std::vector<Point> SkeletalTrapezoidation::discretize(const vd_t::edge_type& vd_edge, const std::vector<Segment>& segments) Points SkeletalTrapezoidation::discretize(const vd_t::edge_type& vd_edge, const std::vector<Segment>& segments)
{ {
/*Terminology in this function assumes that the edge moves horizontally from /*Terminology in this function assumes that the edge moves horizontally from
left to right. This is not necessarily the case; the edge can go in any left to right. This is not necessarily the case; the edge can go in any
@ -257,7 +256,7 @@ std::vector<Point> SkeletalTrapezoidation::discretize(const vd_t::edge_type& vd_
bool point_right = right_cell->contains_point(); bool point_right = right_cell->contains_point();
if ((!point_left && !point_right) || vd_edge.is_secondary()) // Source vert is directly connected to source segment if ((!point_left && !point_right) || vd_edge.is_secondary()) // Source vert is directly connected to source segment
{ {
return std::vector<Point>({ start, end }); return Points({ start, end });
} }
else if (point_left != point_right) //This is a parabolic edge between a point and a line. else if (point_left != point_right) //This is a parabolic edge between a point and a line.
{ {
@ -311,7 +310,7 @@ std::vector<Point> SkeletalTrapezoidation::discretize(const vd_t::edge_type& vd_
//Start generating points along the edge. //Start generating points along the edge.
Point a = start; Point a = start;
Point b = end; Point b = end;
std::vector<Point> ret; Points ret;
ret.emplace_back(a); ret.emplace_back(a);
//Introduce an extra edge at the borders of the markings? //Introduce an extra edge at the borders of the markings?
@ -522,9 +521,11 @@ static bool has_missing_twin_edge(const SkeletalTrapezoidationGraph &graph)
return false; return false;
} }
inline static void rotate_back_skeletal_trapezoidation_graph_after_fix(SkeletalTrapezoidationGraph &graph, using PointMap = SkeletalTrapezoidation::PointMap;
const double fix_angle,
const std::unordered_map<Point, Point, PointHash> &vertex_mapping) inline static void rotate_back_skeletal_trapezoidation_graph_after_fix(SkeletalTrapezoidationGraph &graph,
const double fix_angle,
const PointMap &vertex_mapping)
{ {
for (STHalfEdgeNode &node : graph.nodes) { for (STHalfEdgeNode &node : graph.nodes) {
// If a mapping exists between a rotated point and an original point, use this mapping. Otherwise, rotate a point in the opposite direction. // If a mapping exists between a rotated point and an original point, use this mapping. Otherwise, rotate a point in the opposite direction.
@ -588,7 +589,7 @@ VoronoiDiagramStatus detect_voronoi_diagram_known_issues(const Geometry::Voronoi
return VoronoiDiagramStatus::NO_ISSUE_DETECTED; return VoronoiDiagramStatus::NO_ISSUE_DETECTED;
} }
inline static std::pair<std::unordered_map<Point, Point, PointHash>, double> try_to_fix_degenerated_voronoi_diagram_by_rotation( inline static std::pair<PointMap, double> try_to_fix_degenerated_voronoi_diagram_by_rotation(
Geometry::VoronoiDiagram &voronoi_diagram, Geometry::VoronoiDiagram &voronoi_diagram,
const Polygons &polys, const Polygons &polys,
Polygons &polys_rotated, Polygons &polys_rotated,
@ -597,7 +598,7 @@ inline static std::pair<std::unordered_map<Point, Point, PointHash>, double> try
{ {
const Polygons polys_rotated_original = polys_rotated; const Polygons polys_rotated_original = polys_rotated;
double fixed_by_angle = fix_angles.front(); double fixed_by_angle = fix_angles.front();
std::unordered_map<Point, Point, PointHash> vertex_mapping; PointMap vertex_mapping;
for (const double &fix_angle : fix_angles) { for (const double &fix_angle : fix_angles) {
vertex_mapping.clear(); vertex_mapping.clear();
@ -685,7 +686,7 @@ void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys)
const std::vector<double> fix_angles = {PI / 6, PI / 5, PI / 7, PI / 11}; const std::vector<double> fix_angles = {PI / 6, PI / 5, PI / 7, PI / 11};
double fixed_by_angle = fix_angles.front(); double fixed_by_angle = fix_angles.front();
std::unordered_map<Point, Point, PointHash> vertex_mapping; PointMap vertex_mapping;
// polys_copy is referenced through items stored in the std::vector segments. // polys_copy is referenced through items stored in the std::vector segments.
Polygons polys_copy = polys; Polygons polys_copy = polys;
if (status != VoronoiDiagramStatus::NO_ISSUE_DETECTED) { if (status != VoronoiDiagramStatus::NO_ISSUE_DETECTED) {
@ -813,9 +814,11 @@ process_voronoi_diagram:
edge.from->incident_edge = &edge; edge.from->incident_edge = &edge;
} }
using NodeSet = SkeletalTrapezoidation::NodeSet;
void SkeletalTrapezoidation::separatePointyQuadEndNodes() void SkeletalTrapezoidation::separatePointyQuadEndNodes()
{ {
std::unordered_set<node_t*> visited_nodes; NodeSet visited_nodes;
for (edge_t& edge : graph.edges) for (edge_t& edge : graph.edges)
{ {
if (edge.prev) if (edge.prev)
@ -2285,16 +2288,18 @@ void SkeletalTrapezoidation::addToolpathSegment(const ExtrusionJunction& from, c
void SkeletalTrapezoidation::connectJunctions(ptr_vector_t<LineJunctions>& edge_junctions) void SkeletalTrapezoidation::connectJunctions(ptr_vector_t<LineJunctions>& edge_junctions)
{ {
std::unordered_set<edge_t*> unprocessed_quad_starts(graph.edges.size() * 5 / 2); using EdgeSet = ankerl::unordered_dense::set<edge_t*>;
EdgeSet unprocessed_quad_starts(graph.edges.size() * 5 / 2);
for (edge_t& edge : graph.edges) for (edge_t& edge : graph.edges)
{ {
if (!edge.prev) if (!edge.prev)
{ {
unprocessed_quad_starts.insert(&edge); unprocessed_quad_starts.emplace(&edge);
} }
} }
std::unordered_set<edge_t*> passed_odd_edges; EdgeSet passed_odd_edges;
while (!unprocessed_quad_starts.empty()) while (!unprocessed_quad_starts.empty())
{ {

View File

@ -7,8 +7,10 @@
#include <boost/polygon/voronoi.hpp> #include <boost/polygon/voronoi.hpp>
#include <memory> // smart pointers #include <memory> // smart pointers
#include <unordered_map>
#include <utility> // pair #include <utility> // pair
#include <ankerl/unordered_dense.h>
#include <Arachne/utils/VoronoiUtils.hpp> #include <Arachne/utils/VoronoiUtils.hpp>
#include "utils/HalfEdgeGraph.hpp" #include "utils/HalfEdgeGraph.hpp"
@ -80,7 +82,9 @@ class SkeletalTrapezoidation
const BeadingStrategy& beading_strategy; const BeadingStrategy& beading_strategy;
public: public:
using Segment = PolygonsSegmentIndex; using Segment = PolygonsSegmentIndex;
using PointMap = ankerl::unordered_dense::map<Point, Point, PointHash>;
using NodeSet = ankerl::unordered_dense::set<node_t*>;
/*! /*!
* Construct a new trapezoidation problem to solve. * Construct a new trapezoidation problem to solve.
@ -164,8 +168,8 @@ protected:
* mapping each voronoi VD edge to the corresponding halfedge HE edge * mapping each voronoi VD edge to the corresponding halfedge HE edge
* In case the result segment is discretized, we map the VD edge to the *last* HE edge * In case the result segment is discretized, we map the VD edge to the *last* HE edge
*/ */
std::unordered_map<vd_t::edge_type*, edge_t*> vd_edge_to_he_edge; ankerl::unordered_dense::map<vd_t::edge_type*, edge_t*> vd_edge_to_he_edge;
std::unordered_map<vd_t::vertex_type*, node_t*> vd_node_to_he_node; ankerl::unordered_dense::map<vd_t::vertex_type*, node_t*> vd_node_to_he_node;
node_t& makeNode(vd_t::vertex_type& vd_node, Point p); //!< Get the node which the VD node maps to, or create a new mapping if there wasn't any yet. node_t& makeNode(vd_t::vertex_type& vd_node, Point p); //!< Get the node which the VD node maps to, or create a new mapping if there wasn't any yet.
/*! /*!
@ -204,7 +208,7 @@ protected:
* \return A number of coordinates along the edge where the edge is broken * \return A number of coordinates along the edge where the edge is broken
* up into discrete pieces. * up into discrete pieces.
*/ */
std::vector<Point> discretize(const vd_t::edge_type& segment, const std::vector<Segment>& segments); Points discretize(const vd_t::edge_type& segment, const std::vector<Segment>& segments);
/*! /*!
* Compute the range of line segments that surround a cell of the skeletal * Compute the range of line segments that surround a cell of the skeletal

View File

@ -2,7 +2,8 @@
//CuraEngine is released under the terms of the AGPLv3 or higher. //CuraEngine is released under the terms of the AGPLv3 or higher.
#include "SkeletalTrapezoidationGraph.hpp" #include "SkeletalTrapezoidationGraph.hpp"
#include <unordered_map>
#include <ankerl/unordered_dense.h>
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
@ -180,8 +181,8 @@ bool STHalfEdgeNode::isLocalMaximum(bool strict) const
void SkeletalTrapezoidationGraph::collapseSmallEdges(coord_t snap_dist) void SkeletalTrapezoidationGraph::collapseSmallEdges(coord_t snap_dist)
{ {
std::unordered_map<edge_t*, std::list<edge_t>::iterator> edge_locator; ankerl::unordered_dense::map<edge_t*, Edges::iterator> edge_locator;
std::unordered_map<node_t*, std::list<node_t>::iterator> node_locator; ankerl::unordered_dense::map<node_t*, Nodes::iterator> node_locator;
for (auto edge_it = edges.begin(); edge_it != edges.end(); ++edge_it) for (auto edge_it = edges.begin(); edge_it != edges.end(); ++edge_it)
{ {
@ -193,7 +194,7 @@ void SkeletalTrapezoidationGraph::collapseSmallEdges(coord_t snap_dist)
node_locator.emplace(&*node_it, node_it); node_locator.emplace(&*node_it, node_it);
} }
auto safelyRemoveEdge = [this, &edge_locator](edge_t* to_be_removed, std::list<edge_t>::iterator& current_edge_it, bool& edge_it_is_updated) auto safelyRemoveEdge = [this, &edge_locator](edge_t* to_be_removed, Edges::iterator& current_edge_it, bool& edge_it_is_updated)
{ {
if (current_edge_it != edges.end() if (current_edge_it != edges.end()
&& to_be_removed == &*current_edge_it) && to_be_removed == &*current_edge_it)

View File

@ -2,7 +2,6 @@
// CuraEngine is released under the terms of the AGPLv3 or higher. // CuraEngine is released under the terms of the AGPLv3 or higher.
#include <algorithm> //For std::partition_copy and std::min_element. #include <algorithm> //For std::partition_copy and std::min_element.
#include <unordered_set>
#include "WallToolPaths.hpp" #include "WallToolPaths.hpp"
@ -341,7 +340,7 @@ void removeSmallAreas(Polygons &thiss, const double min_area_size, const bool re
} }
} else { } else {
// For each polygon, computes the signed area, move small outlines at the end of the vector and keep pointer on small holes // For each polygon, computes the signed area, move small outlines at the end of the vector and keep pointer on small holes
std::vector<Polygon> small_holes; Polygons small_holes;
for (auto it = thiss.begin(); it < new_end;) { for (auto it = thiss.begin(); it < new_end;) {
if (double area = ClipperLib::Area(to_path(*it)); fabs(area) < min_area_size) { if (double area = ClipperLib::Area(to_path(*it)); fabs(area) < min_area_size) {
if (area >= 0) { if (area >= 0) {
@ -767,9 +766,9 @@ bool WallToolPaths::removeEmptyToolPaths(std::vector<VariableWidthLines> &toolpa
* *
* \param outer_to_inner Whether the wall polygons with a lower inset_idx should go before those with a higher one. * \param outer_to_inner Whether the wall polygons with a lower inset_idx should go before those with a higher one.
*/ */
std::unordered_set<std::pair<const ExtrusionLine *, const ExtrusionLine *>, boost::hash<std::pair<const ExtrusionLine *, const ExtrusionLine *>>> WallToolPaths::getRegionOrder(const std::vector<ExtrusionLine *> &input, const bool outer_to_inner) WallToolPaths::ExtrusionLineSet WallToolPaths::getRegionOrder(const std::vector<ExtrusionLine *> &input, const bool outer_to_inner)
{ {
std::unordered_set<std::pair<const ExtrusionLine *, const ExtrusionLine *>, boost::hash<std::pair<const ExtrusionLine *, const ExtrusionLine *>>> order_requirements; ExtrusionLineSet order_requirements;
// We build a grid where we map toolpath vertex locations to toolpaths, // We build a grid where we map toolpath vertex locations to toolpaths,
// so that we can easily find which two toolpaths are next to each other, // so that we can easily find which two toolpaths are next to each other,

View File

@ -5,7 +5,8 @@
#define CURAENGINE_WALLTOOLPATHS_H #define CURAENGINE_WALLTOOLPATHS_H
#include <memory> #include <memory>
#include <unordered_set>
#include <ankerl/unordered_dense.h>
#include "BeadingStrategy/BeadingStrategyFactory.hpp" #include "BeadingStrategy/BeadingStrategyFactory.hpp"
#include "utils/ExtrusionLine.hpp" #include "utils/ExtrusionLine.hpp"
@ -73,6 +74,7 @@ public:
*/ */
static bool removeEmptyToolPaths(std::vector<VariableWidthLines> &toolpaths); static bool removeEmptyToolPaths(std::vector<VariableWidthLines> &toolpaths);
using ExtrusionLineSet = ankerl::unordered_dense::set<std::pair<const ExtrusionLine *, const ExtrusionLine *>, boost::hash<std::pair<const ExtrusionLine *, const ExtrusionLine *>>>;
/*! /*!
* Get the order constraints of the insets when printing walls per region / hole. * Get the order constraints of the insets when printing walls per region / hole.
* Each returned pair consists of adjacent wall lines where the left has an inset_idx one lower than the right. * Each returned pair consists of adjacent wall lines where the left has an inset_idx one lower than the right.
@ -81,7 +83,7 @@ public:
* *
* \param outer_to_inner Whether the wall polygons with a lower inset_idx should go before those with a higher one. * \param outer_to_inner Whether the wall polygons with a lower inset_idx should go before those with a higher one.
*/ */
static std::unordered_set<std::pair<const ExtrusionLine *, const ExtrusionLine *>, boost::hash<std::pair<const ExtrusionLine *, const ExtrusionLine *>>> getRegionOrder(const std::vector<ExtrusionLine *> &input, bool outer_to_inner); static ExtrusionLineSet getRegionOrder(const std::vector<ExtrusionLine *> &input, bool outer_to_inner);
protected: protected:
/*! /*!

View File

@ -21,8 +21,10 @@ class HalfEdgeGraph
public: public:
using edge_t = derived_edge_t; using edge_t = derived_edge_t;
using node_t = derived_node_t; using node_t = derived_node_t;
std::list<edge_t> edges; using Edges = std::list<edge_t>;
std::list<node_t> nodes; using Nodes = std::list<node_t>;
Edges edges;
Nodes nodes;
}; };
} // namespace Slic3r::Arachne } // namespace Slic3r::Arachne

View File

@ -7,7 +7,6 @@
#include "SparsePointGrid.hpp" #include "SparsePointGrid.hpp"
#include "PolygonsPointIndex.hpp" #include "PolygonsPointIndex.hpp"
#include "../../Polygon.hpp" #include "../../Polygon.hpp"
#include <unordered_set>
#include <cassert> #include <cassert>
namespace Slic3r::Arachne namespace Slic3r::Arachne

View File

@ -6,7 +6,6 @@
#define UTILS_SPARSE_GRID_H #define UTILS_SPARSE_GRID_H
#include <cassert> #include <cassert>
#include <unordered_map>
#include <vector> #include <vector>
#include <functional> #include <functional>

View File

@ -6,7 +6,6 @@
#define UTILS_SPARSE_LINE_GRID_H #define UTILS_SPARSE_LINE_GRID_H
#include <cassert> #include <cassert>
#include <unordered_map>
#include <vector> #include <vector>
#include <functional> #include <functional>

View File

@ -6,7 +6,6 @@
#define UTILS_SPARSE_POINT_GRID_H #define UTILS_SPARSE_POINT_GRID_H
#include <cassert> #include <cassert>
#include <unordered_map>
#include <vector> #include <vector>
#include "SparseGrid.hpp" #include "SparseGrid.hpp"

View File

@ -7,7 +7,6 @@
#include "../../Point.hpp" #include "../../Point.hpp"
#include <cassert> #include <cassert>
#include <unordered_map>
#include <vector> #include <vector>
#include <functional> #include <functional>

View File

@ -138,9 +138,9 @@ public:
return Point(coord_t(p.x() * matrix[0] + p.y() * matrix[2]), coord_t(p.x() * matrix[1] + p.y() * matrix[3])); return Point(coord_t(p.x() * matrix[0] + p.y() * matrix[2]), coord_t(p.x() * matrix[1] + p.y() * matrix[3]));
} }
}; };
std::vector<Point> VoronoiUtils::discretizeParabola(const Point& p, const Segment& segment, Point s, Point e, coord_t approximate_step_size, float transitioning_angle) Points VoronoiUtils::discretizeParabola(const Point& p, const Segment& segment, Point s, Point e, coord_t approximate_step_size, float transitioning_angle)
{ {
std::vector<Point> discretized; Points discretized;
// x is distance of point projected on the segment ab // x is distance of point projected on the segment ab
// xx is point projected on the segment ab // xx is point projected on the segment ab
const Point a = segment.from(); const Point a = segment.from();

View File

@ -34,7 +34,7 @@ public:
* Discretize a parabola based on (approximate) step size. * Discretize a parabola based on (approximate) step size.
* The \p approximate_step_size is measured parallel to the \p source_segment, not along the parabola. * The \p approximate_step_size is measured parallel to the \p source_segment, not along the parabola.
*/ */
static std::vector<Point> discretizeParabola(const Point &source_point, const Segment &source_segment, Point start, Point end, coord_t approximate_step_size, float transitioning_angle); static Points discretizeParabola(const Point &source_point, const Segment &source_segment, Point start, Point end, coord_t approximate_step_size, float transitioning_angle);
static inline bool is_finite(const VoronoiUtils::vd_t::vertex_type &vertex) static inline bool is_finite(const VoronoiUtils::vd_t::vertex_type &vertex)
{ {

View File

@ -6,23 +6,19 @@
namespace Slic3r { namespace Slic3r {
template BoundingBoxBase<Point>::BoundingBoxBase(const std::vector<Point> &points); template BoundingBoxBase<Point, Points>::BoundingBoxBase(const Points &points);
template BoundingBoxBase<Vec2d>::BoundingBoxBase(const std::vector<Vec2d> &points); template BoundingBoxBase<Vec2d>::BoundingBoxBase(const std::vector<Vec2d> &points);
template BoundingBox3Base<Vec3d>::BoundingBox3Base(const std::vector<Vec3d> &points); template BoundingBox3Base<Vec3d>::BoundingBox3Base(const std::vector<Vec3d> &points);
void BoundingBox::polygon(Polygon* polygon) const void BoundingBox::polygon(Polygon* polygon) const
{ {
polygon->points.clear(); polygon->points = {
polygon->points.resize(4); this->min,
polygon->points[0](0) = this->min(0); { this->max.x(), this->min.y() },
polygon->points[0](1) = this->min(1); this->max,
polygon->points[1](0) = this->max(0); { this->min.x(), this->max.y() }
polygon->points[1](1) = this->min(1); };
polygon->points[2](0) = this->max(0);
polygon->points[2](1) = this->max(1);
polygon->points[3](0) = this->min(0);
polygon->points[3](1) = this->max(1);
} }
Polygon BoundingBox::polygon() const Polygon BoundingBox::polygon() const
@ -37,8 +33,8 @@ BoundingBox BoundingBox::rotated(double angle) const
BoundingBox out; BoundingBox out;
out.merge(this->min.rotated(angle)); out.merge(this->min.rotated(angle));
out.merge(this->max.rotated(angle)); out.merge(this->max.rotated(angle));
out.merge(Point(this->min(0), this->max(1)).rotated(angle)); out.merge(Point(this->min.x(), this->max.y()).rotated(angle));
out.merge(Point(this->max(0), this->min(1)).rotated(angle)); out.merge(Point(this->max.x(), this->min.y()).rotated(angle));
return out; return out;
} }
@ -47,23 +43,23 @@ BoundingBox BoundingBox::rotated(double angle, const Point &center) const
BoundingBox out; BoundingBox out;
out.merge(this->min.rotated(angle, center)); out.merge(this->min.rotated(angle, center));
out.merge(this->max.rotated(angle, center)); out.merge(this->max.rotated(angle, center));
out.merge(Point(this->min(0), this->max(1)).rotated(angle, center)); out.merge(Point(this->min.x(), this->max.y()).rotated(angle, center));
out.merge(Point(this->max(0), this->min(1)).rotated(angle, center)); out.merge(Point(this->max.x(), this->min.y()).rotated(angle, center));
return out; return out;
} }
template <class PointClass> void template <class PointType, typename APointsType> void
BoundingBoxBase<PointClass>::scale(double factor) BoundingBoxBase<PointType, APointsType>::scale(double factor)
{ {
this->min *= factor; this->min *= factor;
this->max *= factor; this->max *= factor;
} }
template void BoundingBoxBase<Point>::scale(double factor); template void BoundingBoxBase<Point, Points>::scale(double factor);
template void BoundingBoxBase<Vec2d>::scale(double factor); template void BoundingBoxBase<Vec2d>::scale(double factor);
template void BoundingBoxBase<Vec3d>::scale(double factor); template void BoundingBoxBase<Vec3d>::scale(double factor);
template <class PointClass> void template <class PointType, typename APointsType> void
BoundingBoxBase<PointClass>::merge(const PointClass &point) BoundingBoxBase<PointType, APointsType>::merge(const PointType &point)
{ {
if (this->defined) { if (this->defined) {
this->min = this->min.cwiseMin(point); this->min = this->min.cwiseMin(point);
@ -74,22 +70,22 @@ BoundingBoxBase<PointClass>::merge(const PointClass &point)
this->defined = true; this->defined = true;
} }
} }
template void BoundingBoxBase<Point>::merge(const Point &point); template void BoundingBoxBase<Point, Points>::merge(const Point &point);
template void BoundingBoxBase<Vec2f>::merge(const Vec2f &point); template void BoundingBoxBase<Vec2f>::merge(const Vec2f &point);
template void BoundingBoxBase<Vec2d>::merge(const Vec2d &point); template void BoundingBoxBase<Vec2d>::merge(const Vec2d &point);
template <class PointClass> void template <class PointType, typename APointsType> void
BoundingBoxBase<PointClass>::merge(const std::vector<PointClass> &points) BoundingBoxBase<PointType, APointsType>::merge(const PointsType &points)
{ {
this->merge(BoundingBoxBase(points)); this->merge(BoundingBoxBase(points));
} }
template void BoundingBoxBase<Point>::merge(const Points &points); template void BoundingBoxBase<Point, Points>::merge(const Points &points);
template void BoundingBoxBase<Vec2d>::merge(const Pointfs &points); template void BoundingBoxBase<Vec2d>::merge(const Pointfs &points);
template <class PointClass> void template <class PointType, typename APointsType> void
BoundingBoxBase<PointClass>::merge(const BoundingBoxBase<PointClass> &bb) BoundingBoxBase<PointType, APointsType>::merge(const BoundingBoxBase<PointType, PointsType> &bb)
{ {
assert(bb.defined || bb.min(0) >= bb.max(0) || bb.min(1) >= bb.max(1)); assert(bb.defined || bb.min.x() >= bb.max.x() || bb.min.y() >= bb.max.y());
if (bb.defined) { if (bb.defined) {
if (this->defined) { if (this->defined) {
this->min = this->min.cwiseMin(bb.min); this->min = this->min.cwiseMin(bb.min);
@ -101,12 +97,12 @@ BoundingBoxBase<PointClass>::merge(const BoundingBoxBase<PointClass> &bb)
} }
} }
} }
template void BoundingBoxBase<Point>::merge(const BoundingBoxBase<Point> &bb); template void BoundingBoxBase<Point, Points>::merge(const BoundingBoxBase<Point, Points> &bb);
template void BoundingBoxBase<Vec2f>::merge(const BoundingBoxBase<Vec2f> &bb); template void BoundingBoxBase<Vec2f>::merge(const BoundingBoxBase<Vec2f> &bb);
template void BoundingBoxBase<Vec2d>::merge(const BoundingBoxBase<Vec2d> &bb); template void BoundingBoxBase<Vec2d>::merge(const BoundingBoxBase<Vec2d> &bb);
template <class PointClass> void template <class PointType> void
BoundingBox3Base<PointClass>::merge(const PointClass &point) BoundingBox3Base<PointType>::merge(const PointType &point)
{ {
if (this->defined) { if (this->defined) {
this->min = this->min.cwiseMin(point); this->min = this->min.cwiseMin(point);
@ -120,17 +116,17 @@ BoundingBox3Base<PointClass>::merge(const PointClass &point)
template void BoundingBox3Base<Vec3f>::merge(const Vec3f &point); template void BoundingBox3Base<Vec3f>::merge(const Vec3f &point);
template void BoundingBox3Base<Vec3d>::merge(const Vec3d &point); template void BoundingBox3Base<Vec3d>::merge(const Vec3d &point);
template <class PointClass> void template <class PointType> void
BoundingBox3Base<PointClass>::merge(const std::vector<PointClass> &points) BoundingBox3Base<PointType>::merge(const PointsType &points)
{ {
this->merge(BoundingBox3Base(points)); this->merge(BoundingBox3Base(points));
} }
template void BoundingBox3Base<Vec3d>::merge(const Pointf3s &points); template void BoundingBox3Base<Vec3d>::merge(const Pointf3s &points);
template <class PointClass> void template <class PointType> void
BoundingBox3Base<PointClass>::merge(const BoundingBox3Base<PointClass> &bb) BoundingBox3Base<PointType>::merge(const BoundingBox3Base<PointType> &bb)
{ {
assert(bb.defined || bb.min(0) >= bb.max(0) || bb.min(1) >= bb.max(1) || bb.min(2) >= bb.max(2)); assert(bb.defined || bb.min.x() >= bb.max.x() || bb.min.y() >= bb.max.y() || bb.min.z() >= bb.max.z());
if (bb.defined) { if (bb.defined) {
if (this->defined) { if (this->defined) {
this->min = this->min.cwiseMin(bb.min); this->min = this->min.cwiseMin(bb.min);
@ -144,83 +140,78 @@ BoundingBox3Base<PointClass>::merge(const BoundingBox3Base<PointClass> &bb)
} }
template void BoundingBox3Base<Vec3d>::merge(const BoundingBox3Base<Vec3d> &bb); template void BoundingBox3Base<Vec3d>::merge(const BoundingBox3Base<Vec3d> &bb);
template <class PointClass> PointClass template <class PointType, typename APointsType> PointType
BoundingBoxBase<PointClass>::size() const BoundingBoxBase<PointType, APointsType>::size() const
{ {
return PointClass(this->max(0) - this->min(0), this->max(1) - this->min(1)); return this->max - this->min;
} }
template Point BoundingBoxBase<Point>::size() const; template Point BoundingBoxBase<Point, Points>::size() const;
template Vec2f BoundingBoxBase<Vec2f>::size() const; template Vec2f BoundingBoxBase<Vec2f>::size() const;
template Vec2d BoundingBoxBase<Vec2d>::size() const; template Vec2d BoundingBoxBase<Vec2d>::size() const;
template <class PointClass> PointClass template <class PointType> PointType
BoundingBox3Base<PointClass>::size() const BoundingBox3Base<PointType>::size() const
{ {
return PointClass(this->max(0) - this->min(0), this->max(1) - this->min(1), this->max(2) - this->min(2)); return this->max - this->min;
} }
template Vec3f BoundingBox3Base<Vec3f>::size() const; template Vec3f BoundingBox3Base<Vec3f>::size() const;
template Vec3d BoundingBox3Base<Vec3d>::size() const; template Vec3d BoundingBox3Base<Vec3d>::size() const;
template <class PointClass> double BoundingBoxBase<PointClass>::radius() const template <class PointType, typename APointsType> double BoundingBoxBase<PointType, APointsType>::radius() const
{ {
assert(this->defined); assert(this->defined);
double x = this->max(0) - this->min(0); return 0.5 * (this->max - this->min).template cast<double>().norm();
double y = this->max(1) - this->min(1);
return 0.5 * sqrt(x*x+y*y);
} }
template double BoundingBoxBase<Point>::radius() const; template double BoundingBoxBase<Point, Points>::radius() const;
template double BoundingBoxBase<Vec2d>::radius() const; template double BoundingBoxBase<Vec2d>::radius() const;
template <class PointClass> double BoundingBox3Base<PointClass>::radius() const template <class PointType> double BoundingBox3Base<PointType>::radius() const
{ {
double x = this->max(0) - this->min(0); return 0.5 * (this->max - this->min).template cast<double>().norm();
double y = this->max(1) - this->min(1);
double z = this->max(2) - this->min(2);
return 0.5 * sqrt(x*x+y*y+z*z);
} }
template double BoundingBox3Base<Vec3d>::radius() const; template double BoundingBox3Base<Vec3d>::radius() const;
template <class PointClass> void template <class PointType, typename APointsType> void
BoundingBoxBase<PointClass>::offset(coordf_t delta) BoundingBoxBase<PointType, APointsType>::offset(coordf_t delta)
{ {
PointClass v(delta, delta); PointType v(delta, delta);
this->min -= v; this->min -= v;
this->max += v; this->max += v;
} }
template void BoundingBoxBase<Point>::offset(coordf_t delta); template void BoundingBoxBase<Point, Points>::offset(coordf_t delta);
template void BoundingBoxBase<Vec2d>::offset(coordf_t delta); template void BoundingBoxBase<Vec2d>::offset(coordf_t delta);
template <class PointClass> void template <class PointType> void
BoundingBox3Base<PointClass>::offset(coordf_t delta) BoundingBox3Base<PointType>::offset(coordf_t delta)
{ {
PointClass v(delta, delta, delta); PointType v(delta, delta, delta);
this->min -= v; this->min -= v;
this->max += v; this->max += v;
} }
template void BoundingBox3Base<Vec3d>::offset(coordf_t delta); template void BoundingBox3Base<Vec3d>::offset(coordf_t delta);
template <class PointClass> PointClass template <class PointType, typename APointsType> PointType
BoundingBoxBase<PointClass>::center() const BoundingBoxBase<PointType, APointsType>::center() const
{ {
return (this->min + this->max) / 2; return (this->min + this->max) / 2;
} }
template Point BoundingBoxBase<Point>::center() const; template Point BoundingBoxBase<Point, Points>::center() const;
template Vec2f BoundingBoxBase<Vec2f>::center() const; template Vec2f BoundingBoxBase<Vec2f>::center() const;
template Vec2d BoundingBoxBase<Vec2d>::center() const; template Vec2d BoundingBoxBase<Vec2d>::center() const;
template <class PointClass> PointClass template <class PointType> PointType
BoundingBox3Base<PointClass>::center() const BoundingBox3Base<PointType>::center() const
{ {
return (this->min + this->max) / 2; return (this->min + this->max) / 2;
} }
template Vec3f BoundingBox3Base<Vec3f>::center() const; template Vec3f BoundingBox3Base<Vec3f>::center() const;
template Vec3d BoundingBox3Base<Vec3d>::center() const; template Vec3d BoundingBox3Base<Vec3d>::center() const;
template <class PointClass> coordf_t template <class PointType> coordf_t
BoundingBox3Base<PointClass>::max_size() const BoundingBox3Base<PointType>::max_size() const
{ {
PointClass s = size(); PointType s = size();
return std::max(s(0), std::max(s(1), s(2))); return std::max(s.x(), std::max(s.y(), s.z()));
} }
template coordf_t BoundingBox3Base<Vec3f>::max_size() const; template coordf_t BoundingBox3Base<Vec3f>::max_size() const;
template coordf_t BoundingBox3Base<Vec3d>::max_size() const; template coordf_t BoundingBox3Base<Vec3d>::max_size() const;
@ -228,8 +219,8 @@ template coordf_t BoundingBox3Base<Vec3d>::max_size() const;
void BoundingBox::align_to_grid(const coord_t cell_size) void BoundingBox::align_to_grid(const coord_t cell_size)
{ {
if (this->defined) { if (this->defined) {
min(0) = Slic3r::align_to_grid(min(0), cell_size); min.x() = Slic3r::align_to_grid(min.x(), cell_size);
min(1) = Slic3r::align_to_grid(min(1), cell_size); min.y() = Slic3r::align_to_grid(min.y(), cell_size);
} }
} }
@ -238,14 +229,14 @@ BoundingBoxf3 BoundingBoxf3::transformed(const Transform3d& matrix) const
typedef Eigen::Matrix<double, 3, 8, Eigen::DontAlign> Vertices; typedef Eigen::Matrix<double, 3, 8, Eigen::DontAlign> Vertices;
Vertices src_vertices; Vertices src_vertices;
src_vertices(0, 0) = min(0); src_vertices(1, 0) = min(1); src_vertices(2, 0) = min(2); src_vertices(0, 0) = min.x(); src_vertices(1, 0) = min.y(); src_vertices(2, 0) = min.z();
src_vertices(0, 1) = max(0); src_vertices(1, 1) = min(1); src_vertices(2, 1) = min(2); src_vertices(0, 1) = max.x(); src_vertices(1, 1) = min.y(); src_vertices(2, 1) = min.z();
src_vertices(0, 2) = max(0); src_vertices(1, 2) = max(1); src_vertices(2, 2) = min(2); src_vertices(0, 2) = max.x(); src_vertices(1, 2) = max.y(); src_vertices(2, 2) = min.z();
src_vertices(0, 3) = min(0); src_vertices(1, 3) = max(1); src_vertices(2, 3) = min(2); src_vertices(0, 3) = min.x(); src_vertices(1, 3) = max.y(); src_vertices(2, 3) = min.z();
src_vertices(0, 4) = min(0); src_vertices(1, 4) = min(1); src_vertices(2, 4) = max(2); src_vertices(0, 4) = min.x(); src_vertices(1, 4) = min.y(); src_vertices(2, 4) = max.z();
src_vertices(0, 5) = max(0); src_vertices(1, 5) = min(1); src_vertices(2, 5) = max(2); src_vertices(0, 5) = max.x(); src_vertices(1, 5) = min.y(); src_vertices(2, 5) = max.z();
src_vertices(0, 6) = max(0); src_vertices(1, 6) = max(1); src_vertices(2, 6) = max(2); src_vertices(0, 6) = max.x(); src_vertices(1, 6) = max.y(); src_vertices(2, 6) = max.z();
src_vertices(0, 7) = min(0); src_vertices(1, 7) = max(1); src_vertices(2, 7) = max(2); src_vertices(0, 7) = min.x(); src_vertices(1, 7) = max.y(); src_vertices(2, 7) = max.z();
Vertices dst_vertices = matrix * src_vertices.colwise().homogeneous(); Vertices dst_vertices = matrix * src_vertices.colwise().homogeneous();

View File

@ -8,53 +8,54 @@
namespace Slic3r { namespace Slic3r {
template <class PointClass> template <typename PointType, typename APointsType = std::vector<PointType>>
class BoundingBoxBase class BoundingBoxBase
{ {
public: public:
PointClass min; using PointsType = APointsType;
PointClass max; PointType min;
PointType max;
bool defined; bool defined;
BoundingBoxBase() : min(PointClass::Zero()), max(PointClass::Zero()), defined(false) {} BoundingBoxBase() : min(PointType::Zero()), max(PointType::Zero()), defined(false) {}
BoundingBoxBase(const PointClass &pmin, const PointClass &pmax) : BoundingBoxBase(const PointType &pmin, const PointType &pmax) :
min(pmin), max(pmax), defined(pmin.x() < pmax.x() && pmin.y() < pmax.y()) {} min(pmin), max(pmax), defined(pmin.x() < pmax.x() && pmin.y() < pmax.y()) {}
BoundingBoxBase(const PointClass &p1, const PointClass &p2, const PointClass &p3) : BoundingBoxBase(const PointType &p1, const PointType &p2, const PointType &p3) :
min(p1), max(p1), defined(false) { merge(p2); merge(p3); } min(p1), max(p1), defined(false) { merge(p2); merge(p3); }
template<class It, class = IteratorOnly<It>> template<class It, class = IteratorOnly<It>>
BoundingBoxBase(It from, It to) BoundingBoxBase(It from, It to)
{ construct(*this, from, to); } { construct(*this, from, to); }
BoundingBoxBase(const std::vector<PointClass> &points) BoundingBoxBase(const PointsType &points)
: BoundingBoxBase(points.begin(), points.end()) : BoundingBoxBase(points.begin(), points.end())
{} {}
void reset() { this->defined = false; this->min = PointClass::Zero(); this->max = PointClass::Zero(); } void reset() { this->defined = false; this->min = PointType::Zero(); this->max = PointType::Zero(); }
void merge(const PointClass &point); void merge(const PointType &point);
void merge(const std::vector<PointClass> &points); void merge(const PointsType &points);
void merge(const BoundingBoxBase<PointClass> &bb); void merge(const BoundingBoxBase<PointType, PointsType> &bb);
void scale(double factor); void scale(double factor);
PointClass size() const; PointType size() const;
double radius() const; double radius() const;
void translate(coordf_t x, coordf_t y) { assert(this->defined); PointClass v(x, y); this->min += v; this->max += v; } void translate(coordf_t x, coordf_t y) { assert(this->defined); PointType v(x, y); this->min += v; this->max += v; }
void translate(const PointClass &v) { this->min += v; this->max += v; } void translate(const PointType &v) { this->min += v; this->max += v; }
void offset(coordf_t delta); void offset(coordf_t delta);
BoundingBoxBase<PointClass> inflated(coordf_t delta) const throw() { BoundingBoxBase<PointClass> out(*this); out.offset(delta); return out; } BoundingBoxBase<PointType, PointsType> inflated(coordf_t delta) const throw() { BoundingBoxBase<PointType, PointsType> out(*this); out.offset(delta); return out; }
PointClass center() const; PointType center() const;
bool contains(const PointClass &point) const { bool contains(const PointType &point) const {
return point.x() >= this->min.x() && point.x() <= this->max.x() return point.x() >= this->min.x() && point.x() <= this->max.x()
&& point.y() >= this->min.y() && point.y() <= this->max.y(); && point.y() >= this->min.y() && point.y() <= this->max.y();
} }
bool contains(const BoundingBoxBase<PointClass> &other) const { bool contains(const BoundingBoxBase<PointType, PointsType> &other) const {
return contains(other.min) && contains(other.max); return contains(other.min) && contains(other.max);
} }
bool overlap(const BoundingBoxBase<PointClass> &other) const { bool overlap(const BoundingBoxBase<PointType, PointsType> &other) const {
return ! (this->max.x() < other.min.x() || this->min.x() > other.max.x() || return ! (this->max.x() < other.min.x() || this->min.x() > other.max.x() ||
this->max.y() < other.min.y() || this->min.y() > other.max.y()); this->max.y() < other.min.y() || this->min.y() > other.max.y());
} }
bool operator==(const BoundingBoxBase<PointClass> &rhs) { return this->min == rhs.min && this->max == rhs.max; } bool operator==(const BoundingBoxBase<PointType, PointsType> &rhs) { return this->min == rhs.min && this->max == rhs.max; }
bool operator!=(const BoundingBoxBase<PointClass> &rhs) { return ! (*this == rhs); } bool operator!=(const BoundingBoxBase<PointType, PointsType> &rhs) { return ! (*this == rhs); }
private: private:
// to access construct() // to access construct()
@ -69,10 +70,10 @@ private:
{ {
if (from != to) { if (from != to) {
auto it = from; auto it = from;
out.min = it->template cast<typename PointClass::Scalar>(); out.min = it->template cast<typename PointType::Scalar>();
out.max = out.min; out.max = out.min;
for (++ it; it != to; ++ it) { for (++ it; it != to; ++ it) {
auto vec = it->template cast<typename PointClass::Scalar>(); auto vec = it->template cast<typename PointType::Scalar>();
out.min = out.min.cwiseMin(vec); out.min = out.min.cwiseMin(vec);
out.max = out.max.cwiseMax(vec); out.max = out.max.cwiseMax(vec);
} }
@ -81,16 +82,18 @@ private:
} }
}; };
template <class PointClass> template <class PointType>
class BoundingBox3Base : public BoundingBoxBase<PointClass> class BoundingBox3Base : public BoundingBoxBase<PointType, std::vector<PointType>>
{ {
public: public:
BoundingBox3Base() : BoundingBoxBase<PointClass>() {} using PointsType = std::vector<PointType>;
BoundingBox3Base(const PointClass &pmin, const PointClass &pmax) :
BoundingBoxBase<PointClass>(pmin, pmax) BoundingBox3Base() : BoundingBoxBase<PointType>() {}
{ if (pmin.z() >= pmax.z()) BoundingBoxBase<PointClass>::defined = false; } BoundingBox3Base(const PointType &pmin, const PointType &pmax) :
BoundingBox3Base(const PointClass &p1, const PointClass &p2, const PointClass &p3) : BoundingBoxBase<PointType>(pmin, pmax)
BoundingBoxBase<PointClass>(p1, p1) { merge(p2); merge(p3); } { if (pmin.z() >= pmax.z()) BoundingBoxBase<PointType>::defined = false; }
BoundingBox3Base(const PointType &p1, const PointType &p2, const PointType &p3) :
BoundingBoxBase<PointType>(p1, p1) { merge(p2); merge(p3); }
template<class It, class = IteratorOnly<It> > BoundingBox3Base(It from, It to) template<class It, class = IteratorOnly<It> > BoundingBox3Base(It from, It to)
{ {
@ -98,67 +101,67 @@ public:
throw Slic3r::InvalidArgument("Empty point set supplied to BoundingBox3Base constructor"); throw Slic3r::InvalidArgument("Empty point set supplied to BoundingBox3Base constructor");
auto it = from; auto it = from;
this->min = it->template cast<typename PointClass::Scalar>(); this->min = it->template cast<typename PointType::Scalar>();
this->max = this->min; this->max = this->min;
for (++ it; it != to; ++ it) { for (++ it; it != to; ++ it) {
auto vec = it->template cast<typename PointClass::Scalar>(); auto vec = it->template cast<typename PointType::Scalar>();
this->min = this->min.cwiseMin(vec); this->min = this->min.cwiseMin(vec);
this->max = this->max.cwiseMax(vec); this->max = this->max.cwiseMax(vec);
} }
this->defined = (this->min.x() < this->max.x()) && (this->min.y() < this->max.y()) && (this->min.z() < this->max.z()); this->defined = (this->min.x() < this->max.x()) && (this->min.y() < this->max.y()) && (this->min.z() < this->max.z());
} }
BoundingBox3Base(const std::vector<PointClass> &points) BoundingBox3Base(const PointsType &points)
: BoundingBox3Base(points.begin(), points.end()) : BoundingBox3Base(points.begin(), points.end())
{} {}
void merge(const PointClass &point); void merge(const PointType &point);
void merge(const std::vector<PointClass> &points); void merge(const PointsType &points);
void merge(const BoundingBox3Base<PointClass> &bb); void merge(const BoundingBox3Base<PointType> &bb);
PointClass size() const; PointType size() const;
double radius() const; double radius() const;
void translate(coordf_t x, coordf_t y, coordf_t z) { assert(this->defined); PointClass v(x, y, z); this->min += v; this->max += v; } void translate(coordf_t x, coordf_t y, coordf_t z) { assert(this->defined); PointType v(x, y, z); this->min += v; this->max += v; }
void translate(const Vec3d &v) { this->min += v; this->max += v; } void translate(const Vec3d &v) { this->min += v; this->max += v; }
void offset(coordf_t delta); void offset(coordf_t delta);
BoundingBox3Base<PointClass> inflated(coordf_t delta) const throw() { BoundingBox3Base<PointClass> out(*this); out.offset(delta); return out; } BoundingBox3Base<PointType> inflated(coordf_t delta) const throw() { BoundingBox3Base<PointType> out(*this); out.offset(delta); return out; }
PointClass center() const; PointType center() const;
coordf_t max_size() const; coordf_t max_size() const;
bool contains(const PointClass &point) const { bool contains(const PointType &point) const {
return BoundingBoxBase<PointClass>::contains(point) && point.z() >= this->min.z() && point.z() <= this->max.z(); return BoundingBoxBase<PointType>::contains(point) && point.z() >= this->min.z() && point.z() <= this->max.z();
} }
bool contains(const BoundingBox3Base<PointClass>& other) const { bool contains(const BoundingBox3Base<PointType>& other) const {
return contains(other.min) && contains(other.max); return contains(other.min) && contains(other.max);
} }
// Intersects without boundaries. // Intersects without boundaries.
bool intersects(const BoundingBox3Base<PointClass>& other) const { bool intersects(const BoundingBox3Base<PointType>& other) const {
return this->min.x() < other.max.x() && this->max.x() > other.min.x() && this->min.y() < other.max.y() && this->max.y() > other.min.y() && return this->min.x() < other.max.x() && this->max.x() > other.min.x() && this->min.y() < other.max.y() && this->max.y() > other.min.y() &&
this->min.z() < other.max.z() && this->max.z() > other.min.z(); this->min.z() < other.max.z() && this->max.z() > other.min.z();
} }
}; };
// Will prevent warnings caused by non existing definition of template in hpp // Will prevent warnings caused by non existing definition of template in hpp
extern template void BoundingBoxBase<Point>::scale(double factor); extern template void BoundingBoxBase<Point, Points>::scale(double factor);
extern template void BoundingBoxBase<Vec2d>::scale(double factor); extern template void BoundingBoxBase<Vec2d>::scale(double factor);
extern template void BoundingBoxBase<Vec3d>::scale(double factor); extern template void BoundingBoxBase<Vec3d>::scale(double factor);
extern template void BoundingBoxBase<Point>::offset(coordf_t delta); extern template void BoundingBoxBase<Point, Points>::offset(coordf_t delta);
extern template void BoundingBoxBase<Vec2d>::offset(coordf_t delta); extern template void BoundingBoxBase<Vec2d>::offset(coordf_t delta);
extern template void BoundingBoxBase<Point>::merge(const Point &point); extern template void BoundingBoxBase<Point, Points>::merge(const Point &point);
extern template void BoundingBoxBase<Vec2f>::merge(const Vec2f &point); extern template void BoundingBoxBase<Vec2f>::merge(const Vec2f &point);
extern template void BoundingBoxBase<Vec2d>::merge(const Vec2d &point); extern template void BoundingBoxBase<Vec2d>::merge(const Vec2d &point);
extern template void BoundingBoxBase<Point>::merge(const Points &points); extern template void BoundingBoxBase<Point, Points>::merge(const Points &points);
extern template void BoundingBoxBase<Vec2d>::merge(const Pointfs &points); extern template void BoundingBoxBase<Vec2d>::merge(const Pointfs &points);
extern template void BoundingBoxBase<Point>::merge(const BoundingBoxBase<Point> &bb); extern template void BoundingBoxBase<Point, Points>::merge(const BoundingBoxBase<Point, Points> &bb);
extern template void BoundingBoxBase<Vec2f>::merge(const BoundingBoxBase<Vec2f> &bb); extern template void BoundingBoxBase<Vec2f>::merge(const BoundingBoxBase<Vec2f> &bb);
extern template void BoundingBoxBase<Vec2d>::merge(const BoundingBoxBase<Vec2d> &bb); extern template void BoundingBoxBase<Vec2d>::merge(const BoundingBoxBase<Vec2d> &bb);
extern template Point BoundingBoxBase<Point>::size() const; extern template Point BoundingBoxBase<Point, Points>::size() const;
extern template Vec2f BoundingBoxBase<Vec2f>::size() const; extern template Vec2f BoundingBoxBase<Vec2f>::size() const;
extern template Vec2d BoundingBoxBase<Vec2d>::size() const; extern template Vec2d BoundingBoxBase<Vec2d>::size() const;
extern template double BoundingBoxBase<Point>::radius() const; extern template double BoundingBoxBase<Point, Points>::radius() const;
extern template double BoundingBoxBase<Vec2d>::radius() const; extern template double BoundingBoxBase<Vec2d>::radius() const;
extern template Point BoundingBoxBase<Point>::center() const; extern template Point BoundingBoxBase<Point, Points>::center() const;
extern template Vec2f BoundingBoxBase<Vec2f>::center() const; extern template Vec2f BoundingBoxBase<Vec2f>::center() const;
extern template Vec2d BoundingBoxBase<Vec2d>::center() const; extern template Vec2d BoundingBoxBase<Vec2d>::center() const;
extern template void BoundingBox3Base<Vec3f>::merge(const Vec3f &point); extern template void BoundingBox3Base<Vec3f>::merge(const Vec3f &point);
@ -174,7 +177,7 @@ extern template Vec3d BoundingBox3Base<Vec3d>::center() const;
extern template coordf_t BoundingBox3Base<Vec3f>::max_size() const; extern template coordf_t BoundingBox3Base<Vec3f>::max_size() const;
extern template coordf_t BoundingBox3Base<Vec3d>::max_size() const; extern template coordf_t BoundingBox3Base<Vec3d>::max_size() const;
class BoundingBox : public BoundingBoxBase<Point> class BoundingBox : public BoundingBoxBase<Point, Points>
{ {
public: public:
void polygon(Polygon* polygon) const; void polygon(Polygon* polygon) const;
@ -187,9 +190,9 @@ public:
// to encompass the original bounding box. // to encompass the original bounding box.
void align_to_grid(const coord_t cell_size); void align_to_grid(const coord_t cell_size);
BoundingBox() : BoundingBoxBase<Point>() {} BoundingBox() : BoundingBoxBase<Point, Points>() {}
BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase<Point>(pmin, pmax) {} BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase<Point, Points>(pmin, pmax) {}
BoundingBox(const Points &points) : BoundingBoxBase<Point>(points) {} BoundingBox(const Points &points) : BoundingBoxBase<Point, Points>(points) {}
BoundingBox inflated(coordf_t delta) const throw() { BoundingBox out(*this); out.offset(delta); return out; } BoundingBox inflated(coordf_t delta) const throw() { BoundingBox out(*this); out.offset(delta); return out; }
@ -222,14 +225,14 @@ public:
BoundingBoxf3 transformed(const Transform3d& matrix) const; BoundingBoxf3 transformed(const Transform3d& matrix) const;
}; };
template<typename VT> template<typename PointType, typename PointsType>
inline bool empty(const BoundingBoxBase<VT> &bb) inline bool empty(const BoundingBoxBase<PointType, PointsType> &bb)
{ {
return ! bb.defined || bb.min.x() >= bb.max.x() || bb.min.y() >= bb.max.y(); return ! bb.defined || bb.min.x() >= bb.max.x() || bb.min.y() >= bb.max.y();
} }
template<typename VT> template<typename PointType>
inline bool empty(const BoundingBox3Base<VT> &bb) inline bool empty(const BoundingBox3Base<PointType> &bb)
{ {
return ! bb.defined || bb.min.x() >= bb.max.x() || bb.min.y() >= bb.max.y() || bb.min.z() >= bb.max.z(); return ! bb.defined || bb.min.x() >= bb.max.x() || bb.min.y() >= bb.max.y() || bb.min.z() >= bb.max.z();
} }

View File

@ -127,8 +127,8 @@ set(SLIC3R_SOURCES
Format/SL1.cpp Format/SL1.cpp
Format/SL1_SVG.hpp Format/SL1_SVG.hpp
Format/SL1_SVG.cpp Format/SL1_SVG.cpp
Format/pwmx.hpp Format/AnycubicSLA.hpp
Format/pwmx.cpp Format/AnycubicSLA.cpp
Format/STEP.hpp Format/STEP.hpp
Format/STEP.cpp Format/STEP.cpp
GCode/ThumbnailData.cpp GCode/ThumbnailData.cpp
@ -485,6 +485,7 @@ target_link_libraries(libslic3r
qhull qhull
semver semver
TBB::tbb TBB::tbb
TBB::tbbmalloc
libslic3r_cgal libslic3r_cgal
${CMAKE_DL_LIBS} ${CMAKE_DL_LIBS}
PNG::PNG PNG::PNG

View File

@ -1,6 +1,21 @@
#include "ClipperUtils.hpp" #include "ClipperUtils.hpp"
#include "Geometry.hpp" #include "Geometry.hpp"
#include "ShortestPath.hpp" #include "ShortestPath.hpp"
#include "Utils.hpp"
// #define CLIPPER_UTILS_TIMING
#ifdef CLIPPER_UTILS_TIMING
// time limit for one ClipperLib operation (union / diff / offset), in ms
#define CLIPPER_UTILS_TIME_LIMIT_DEFAULT 50
#include <boost/current_function.hpp>
#include "Timer.hpp"
#define CLIPPER_UTILS_TIME_LIMIT_SECONDS(limit) Timing::TimeLimitAlarm time_limit_alarm(uint64_t(limit) * 1000000000l, BOOST_CURRENT_FUNCTION)
#define CLIPPER_UTILS_TIME_LIMIT_MILLIS(limit) Timing::TimeLimitAlarm time_limit_alarm(uint64_t(limit) * 1000000l, BOOST_CURRENT_FUNCTION)
#else
#define CLIPPER_UTILS_TIME_LIMIT_SECONDS(limit) do {} while(false)
#define CLIPPER_UTILS_TIME_LIMIT_MILLIS(limit) do {} while(false)
#endif // CLIPPER_UTILS_TIMING
// #define CLIPPER_UTILS_DEBUG // #define CLIPPER_UTILS_DEBUG
@ -50,9 +65,11 @@ namespace ClipperUtils {
// Clip source polygon to be used as a clipping polygon with a bouding box around the source (to be clipped) polygon. // Clip source polygon to be used as a clipping polygon with a bouding box around the source (to be clipped) polygon.
// Useful as an optimization for expensive ClipperLib operations, for example when clipping source polygons one by one // Useful as an optimization for expensive ClipperLib operations, for example when clipping source polygons one by one
// with a set of polygons covering the whole layer below. // with a set of polygons covering the whole layer below.
template<typename PointType> template<typename PointsType>
inline void clip_clipper_polygon_with_subject_bbox_templ(const std::vector<PointType> &src, const BoundingBox &bbox, std::vector<PointType> &out) inline void clip_clipper_polygon_with_subject_bbox_templ(const PointsType &src, const BoundingBox &bbox, PointsType &out)
{ {
using PointType = typename PointsType::value_type;
out.clear(); out.clear();
const size_t cnt = src.size(); const size_t cnt = src.size();
if (cnt < 3) if (cnt < 3)
@ -107,10 +124,10 @@ namespace ClipperUtils {
void clip_clipper_polygon_with_subject_bbox(const ZPoints &src, const BoundingBox &bbox, ZPoints &out) void clip_clipper_polygon_with_subject_bbox(const ZPoints &src, const BoundingBox &bbox, ZPoints &out)
{ clip_clipper_polygon_with_subject_bbox_templ(src, bbox, out); } { clip_clipper_polygon_with_subject_bbox_templ(src, bbox, out); }
template<typename PointType> template<typename PointsType>
[[nodiscard]] std::vector<PointType> clip_clipper_polygon_with_subject_bbox_templ(const std::vector<PointType> &src, const BoundingBox &bbox) [[nodiscard]] PointsType clip_clipper_polygon_with_subject_bbox_templ(const PointsType &src, const BoundingBox &bbox)
{ {
std::vector<PointType> out; PointsType out;
clip_clipper_polygon_with_subject_bbox(src, bbox, out); clip_clipper_polygon_with_subject_bbox(src, bbox, out);
return out; return out;
} }
@ -257,6 +274,8 @@ bool has_duplicate_points(const ClipperLib::PolyTree &polytree)
template<typename PathsProvider, ClipperLib::EndType endType = ClipperLib::etClosedPolygon> template<typename PathsProvider, ClipperLib::EndType endType = ClipperLib::etClosedPolygon>
static ClipperLib::Paths raw_offset(PathsProvider &&paths, float offset, ClipperLib::JoinType joinType, double miterLimit) static ClipperLib::Paths raw_offset(PathsProvider &&paths, float offset, ClipperLib::JoinType joinType, double miterLimit)
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
ClipperLib::ClipperOffset co; ClipperLib::ClipperOffset co;
ClipperLib::Paths out; ClipperLib::Paths out;
out.reserve(paths.size()); out.reserve(paths.size());
@ -298,6 +317,8 @@ TResult clipper_do(
TClip && clip, TClip && clip,
const ClipperLib::PolyFillType fillType) const ClipperLib::PolyFillType fillType)
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
ClipperLib::Clipper clipper; ClipperLib::Clipper clipper;
clipper.AddPaths(std::forward<TSubj>(subject), ClipperLib::ptSubject, true); clipper.AddPaths(std::forward<TSubj>(subject), ClipperLib::ptSubject, true);
clipper.AddPaths(std::forward<TClip>(clip), ClipperLib::ptClip, true); clipper.AddPaths(std::forward<TClip>(clip), ClipperLib::ptClip, true);
@ -327,6 +348,8 @@ TResult clipper_union(
// fillType pftNonZero and pftPositive "should" produce the same result for "normalized with implicit union" set of polygons // fillType pftNonZero and pftPositive "should" produce the same result for "normalized with implicit union" set of polygons
const ClipperLib::PolyFillType fillType = ClipperLib::pftNonZero) const ClipperLib::PolyFillType fillType = ClipperLib::pftNonZero)
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
ClipperLib::Clipper clipper; ClipperLib::Clipper clipper;
clipper.AddPaths(std::forward<TSubj>(subject), ClipperLib::ptSubject, true); clipper.AddPaths(std::forward<TSubj>(subject), ClipperLib::ptSubject, true);
TResult retval; TResult retval;
@ -365,6 +388,8 @@ template<> void remove_outermost_polygon<ClipperLib::PolyTree>(ClipperLib::PolyT
template<class TResult, typename PathsProvider> template<class TResult, typename PathsProvider>
static TResult shrink_paths(PathsProvider &&paths, float offset, ClipperLib::JoinType joinType, double miterLimit) static TResult shrink_paths(PathsProvider &&paths, float offset, ClipperLib::JoinType joinType, double miterLimit)
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
assert(offset > 0); assert(offset > 0);
TResult out; TResult out;
if (auto raw = raw_offset(std::forward<PathsProvider>(paths), - offset, joinType, miterLimit); ! raw.empty()) { if (auto raw = raw_offset(std::forward<PathsProvider>(paths), - offset, joinType, miterLimit); ! raw.empty()) {
@ -404,6 +429,8 @@ Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, C
// returns number of expolygons collected (0 or 1). // returns number of expolygons collected (0 or 1).
static int offset_expolygon_inner(const Slic3r::ExPolygon &expoly, const float delta, ClipperLib::JoinType joinType, double miterLimit, ClipperLib::Paths &out) static int offset_expolygon_inner(const Slic3r::ExPolygon &expoly, const float delta, ClipperLib::JoinType joinType, double miterLimit, ClipperLib::Paths &out)
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
// 1) Offset the outer contour. // 1) Offset the outer contour.
ClipperLib::Paths contours; ClipperLib::Paths contours;
{ {
@ -612,6 +639,8 @@ inline ClipperLib::PolyTree clipper_do_polytree(
PathProvider2 &&clip, PathProvider2 &&clip,
const ClipperLib::PolyFillType fillType) const ClipperLib::PolyFillType fillType)
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
// Perform the operation with the output to input_subject. // Perform the operation with the output to input_subject.
// This pass does not generate a PolyTree, which is a very expensive operation with the current Clipper library // This pass does not generate a PolyTree, which is a very expensive operation with the current Clipper library
// if there are overapping edges. // if there are overapping edges.
@ -750,6 +779,8 @@ Slic3r::ExPolygons union_ex(const Slic3r::Surfaces &subject)
template<typename PathsProvider1, typename PathsProvider2> template<typename PathsProvider1, typename PathsProvider2>
Polylines _clipper_pl_open(ClipperLib::ClipType clipType, PathsProvider1 &&subject, PathsProvider2 &&clip) Polylines _clipper_pl_open(ClipperLib::ClipType clipType, PathsProvider1 &&subject, PathsProvider2 &&clip)
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
ClipperLib::Clipper clipper; ClipperLib::Clipper clipper;
clipper.AddPaths(std::forward<PathsProvider1>(subject), ClipperLib::ptSubject, false); clipper.AddPaths(std::forward<PathsProvider1>(subject), ClipperLib::ptSubject, false);
clipper.AddPaths(std::forward<PathsProvider2>(clip), ClipperLib::ptClip, true); clipper.AddPaths(std::forward<PathsProvider2>(clip), ClipperLib::ptClip, true);
@ -935,6 +966,8 @@ Polygons union_pt_chained_outside_in(const Polygons &subject)
Polygons simplify_polygons(const Polygons &subject, bool preserve_collinear) Polygons simplify_polygons(const Polygons &subject, bool preserve_collinear)
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
ClipperLib::Paths output; ClipperLib::Paths output;
if (preserve_collinear) { if (preserve_collinear) {
ClipperLib::Clipper c; ClipperLib::Clipper c;
@ -952,6 +985,8 @@ Polygons simplify_polygons(const Polygons &subject, bool preserve_collinear)
ExPolygons simplify_polygons_ex(const Polygons &subject, bool preserve_collinear) ExPolygons simplify_polygons_ex(const Polygons &subject, bool preserve_collinear)
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
if (! preserve_collinear) if (! preserve_collinear)
return union_ex(simplify_polygons(subject, false)); return union_ex(simplify_polygons(subject, false));
@ -968,6 +1003,8 @@ ExPolygons simplify_polygons_ex(const Polygons &subject, bool preserve_collinear
Polygons top_level_islands(const Slic3r::Polygons &polygons) Polygons top_level_islands(const Slic3r::Polygons &polygons)
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
// init Clipper // init Clipper
ClipperLib::Clipper clipper; ClipperLib::Clipper clipper;
clipper.Clear(); clipper.Clear();
@ -991,6 +1028,8 @@ ClipperLib::Paths fix_after_outer_offset(
ClipperLib::PolyFillType filltype, // = ClipperLib::pftPositive ClipperLib::PolyFillType filltype, // = ClipperLib::pftPositive
bool reverse_result) // = false bool reverse_result) // = false
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
ClipperLib::Paths solution; ClipperLib::Paths solution;
if (! input.empty()) { if (! input.empty()) {
ClipperLib::Clipper clipper; ClipperLib::Clipper clipper;
@ -1009,6 +1048,8 @@ ClipperLib::Paths fix_after_inner_offset(
ClipperLib::PolyFillType filltype, // = ClipperLib::pftNegative ClipperLib::PolyFillType filltype, // = ClipperLib::pftNegative
bool reverse_result) // = true bool reverse_result) // = true
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
ClipperLib::Paths solution; ClipperLib::Paths solution;
if (! input.empty()) { if (! input.empty()) {
ClipperLib::Clipper clipper; ClipperLib::Clipper clipper;
@ -1029,6 +1070,8 @@ ClipperLib::Paths fix_after_inner_offset(
ClipperLib::Path mittered_offset_path_scaled(const Points &contour, const std::vector<float> &deltas, double miter_limit) ClipperLib::Path mittered_offset_path_scaled(const Points &contour, const std::vector<float> &deltas, double miter_limit)
{ {
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
assert(contour.size() == deltas.size()); assert(contour.size() == deltas.size());
#ifndef NDEBUG #ifndef NDEBUG
@ -1167,34 +1210,49 @@ ClipperLib::Path mittered_offset_path_scaled(const Points &contour, const std::v
return out; return out;
} }
static void variable_offset_inner_raw(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit, ClipperLib::Paths &contours, ClipperLib::Paths &holes)
{
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
#ifndef NDEBUG
// Verify that the deltas are all non positive.
for (const std::vector<float> &ds : deltas)
for (float delta : ds)
assert(delta <= 0.);
assert(expoly.holes.size() + 1 == deltas.size());
assert(ClipperLib::Area(expoly.contour.points) > 0.);
for (auto &h : expoly.holes)
assert(ClipperLib::Area(h.points) < 0.);
#endif /* NDEBUG */
// 1) Offset the outer contour.
contours = fix_after_inner_offset(mittered_offset_path_scaled(expoly.contour.points, deltas.front(), miter_limit), ClipperLib::pftNegative, true);
#ifndef NDEBUG
// Shrinking a contour may split it into pieces, but never create a new hole inside the contour.
for (auto &c : contours)
assert(ClipperLib::Area(c) > 0.);
#endif /* NDEBUG */
// 2) Offset the holes one by one, collect the results.
holes.reserve(expoly.holes.size());
for (const Polygon &hole : expoly.holes)
append(holes, fix_after_outer_offset(mittered_offset_path_scaled(hole.points, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftNegative, false));
#ifndef NDEBUG
// Offsetting a hole curve of a C shape may close the C into a ring with a new hole inside, thus creating a hole inside a hole shape, thus a hole will be created with negative area
// and the following test will fail.
// for (auto &c : holes)
// assert(ClipperLib::Area(c) > 0.);
#endif /* NDEBUG */
}
Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit) Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit)
{ {
#ifndef NDEBUG CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
// Verify that the deltas are all non positive.
for (const std::vector<float> &ds : deltas)
for (float delta : ds)
assert(delta <= 0.);
assert(expoly.holes.size() + 1 == deltas.size());
#endif /* NDEBUG */
// 1) Offset the outer contour. ClipperLib::Paths contours, holes;
ClipperLib::Paths contours = fix_after_inner_offset(mittered_offset_path_scaled(expoly.contour.points, deltas.front(), miter_limit), ClipperLib::pftNegative, true); variable_offset_inner_raw(expoly, deltas, miter_limit, contours, holes);
#ifndef NDEBUG
for (auto &c : contours)
assert(ClipperLib::Area(c) > 0.);
#endif /* NDEBUG */
// 2) Offset the holes one by one, collect the results. // Subtract holes from the contours.
ClipperLib::Paths holes;
holes.reserve(expoly.holes.size());
for (const Polygon& hole : expoly.holes)
append(holes, fix_after_outer_offset(mittered_offset_path_scaled(hole.points, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftNegative, false));
#ifndef NDEBUG
for (auto &c : holes)
assert(ClipperLib::Area(c) > 0.);
#endif /* NDEBUG */
// 3) Subtract holes from the contours.
ClipperLib::Paths output; ClipperLib::Paths output;
if (holes.empty()) if (holes.empty())
output = std::move(contours); output = std::move(contours);
@ -1202,6 +1260,8 @@ Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector<std::v
ClipperLib::Clipper clipper; ClipperLib::Clipper clipper;
clipper.Clear(); clipper.Clear();
clipper.AddPaths(contours, ClipperLib::ptSubject, true); clipper.AddPaths(contours, ClipperLib::ptSubject, true);
// Holes may contain holes in holes produced by expanding a C hole shape.
// The situation is processed correctly by Clipper diff operation.
clipper.AddPaths(holes, ClipperLib::ptClip, true); clipper.AddPaths(holes, ClipperLib::ptClip, true);
clipper.Execute(ClipperLib::ctDifference, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero); clipper.Execute(ClipperLib::ctDifference, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
} }
@ -1209,129 +1269,128 @@ Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector<std::v
return to_polygons(std::move(output)); return to_polygons(std::move(output));
} }
ExPolygons variable_offset_inner_ex(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit)
{
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
ClipperLib::Paths contours, holes;
variable_offset_inner_raw(expoly, deltas, miter_limit, contours, holes);
// Subtract holes from the contours.
ExPolygons output;
if (holes.empty()) {
output.reserve(contours.size());
// Shrinking a CCW contour may only produce more CCW contours, but never new holes.
for (ClipperLib::Path &path : contours)
output.emplace_back(std::move(path));
} else {
ClipperLib::Clipper clipper;
clipper.AddPaths(contours, ClipperLib::ptSubject, true);
// Holes may contain holes in holes produced by expanding a C hole shape.
// The situation is processed correctly by Clipper diff operation, producing concentric expolygons.
clipper.AddPaths(holes, ClipperLib::ptClip, true);
ClipperLib::PolyTree polytree;
clipper.Execute(ClipperLib::ctDifference, polytree, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
output = PolyTreeToExPolygons(std::move(polytree));
}
return output;
}
static void variable_offset_outer_raw(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit, ClipperLib::Paths &contours, ClipperLib::Paths &holes)
{
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
#ifndef NDEBUG
// Verify that the deltas are all non positive.
for (const std::vector<float> &ds : deltas)
for (float delta : ds)
assert(delta >= 0.);
assert(expoly.holes.size() + 1 == deltas.size());
assert(ClipperLib::Area(expoly.contour.points) > 0.);
for (auto &h : expoly.holes)
assert(ClipperLib::Area(h.points) < 0.);
#endif /* NDEBUG */
// 1) Offset the outer contour.
contours = fix_after_outer_offset(mittered_offset_path_scaled(expoly.contour.points, deltas.front(), miter_limit), ClipperLib::pftPositive, false);
// Inflating a contour must not remove it.
assert(contours.size() >= 1);
#ifndef NDEBUG
// Offsetting a positive curve of a C shape may close the C into a ring with hole shape, thus a hole will be created with negative area
// and the following test will fail.
// for (auto &c : contours)
// assert(ClipperLib::Area(c) > 0.);
#endif /* NDEBUG */
// 2) Offset the holes one by one, collect the results.
holes.reserve(expoly.holes.size());
for (const Polygon& hole : expoly.holes)
append(holes, fix_after_inner_offset(mittered_offset_path_scaled(hole.points, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftPositive, true));
#ifndef NDEBUG
// Shrinking a hole may split it into pieces, but never create a new hole inside a hole.
for (auto &c : holes)
assert(ClipperLib::Area(c) > 0.);
#endif /* NDEBUG */
}
Polygons variable_offset_outer(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit) Polygons variable_offset_outer(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit)
{ {
#ifndef NDEBUG CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
// Verify that the deltas are all non positive.
for (const std::vector<float>& ds : deltas)
for (float delta : ds)
assert(delta >= 0.);
assert(expoly.holes.size() + 1 == deltas.size());
#endif /* NDEBUG */
// 1) Offset the outer contour. ClipperLib::Paths contours, holes;
ClipperLib::Paths contours = fix_after_outer_offset(mittered_offset_path_scaled(expoly.contour.points, deltas.front(), miter_limit), ClipperLib::pftPositive, false); variable_offset_outer_raw(expoly, deltas, miter_limit, contours, holes);
#ifndef NDEBUG
for (auto &c : contours)
assert(ClipperLib::Area(c) > 0.);
#endif /* NDEBUG */
// 2) Offset the holes one by one, collect the results. // Subtract holes from the contours.
ClipperLib::Paths holes; ClipperLib::Paths output;
holes.reserve(expoly.holes.size()); if (holes.empty())
for (const Polygon& hole : expoly.holes) output = std::move(contours);
append(holes, fix_after_inner_offset(mittered_offset_path_scaled(hole.points, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftPositive, true)); else {
#ifndef NDEBUG //FIXME the difference is not needed as the holes may never intersect with other holes.
for (auto &c : holes) ClipperLib::Clipper clipper;
assert(ClipperLib::Area(c) > 0.); clipper.Clear();
#endif /* NDEBUG */ clipper.AddPaths(contours, ClipperLib::ptSubject, true);
clipper.AddPaths(holes, ClipperLib::ptClip, true);
clipper.Execute(ClipperLib::ctDifference, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
}
// 3) Subtract holes from the contours. return to_polygons(std::move(output));
ClipperLib::Paths output;
if (holes.empty())
output = std::move(contours);
else {
ClipperLib::Clipper clipper;
clipper.Clear();
clipper.AddPaths(contours, ClipperLib::ptSubject, true);
clipper.AddPaths(holes, ClipperLib::ptClip, true);
clipper.Execute(ClipperLib::ctDifference, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
}
return to_polygons(std::move(output));
} }
ExPolygons variable_offset_outer_ex(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit) ExPolygons variable_offset_outer_ex(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit)
{ {
#ifndef NDEBUG CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
// Verify that the deltas are all non positive.
for (const std::vector<float>& ds : deltas)
for (float delta : ds)
assert(delta >= 0.);
assert(expoly.holes.size() + 1 == deltas.size());
#endif /* NDEBUG */
// 1) Offset the outer contour. ClipperLib::Paths contours, holes;
ClipperLib::Paths contours = fix_after_outer_offset(mittered_offset_path_scaled(expoly.contour.points, deltas.front(), miter_limit), ClipperLib::pftPositive, false); variable_offset_outer_raw(expoly, deltas, miter_limit, contours, holes);
#ifndef NDEBUG
for (auto &c : contours)
assert(ClipperLib::Area(c) > 0.);
#endif /* NDEBUG */
// 2) Offset the holes one by one, collect the results. // Subtract holes from the contours.
ClipperLib::Paths holes;
holes.reserve(expoly.holes.size());
for (const Polygon& hole : expoly.holes)
append(holes, fix_after_inner_offset(mittered_offset_path_scaled(hole.points, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftPositive, true));
#ifndef NDEBUG
for (auto &c : holes)
assert(ClipperLib::Area(c) > 0.);
#endif /* NDEBUG */
// 3) Subtract holes from the contours.
ExPolygons output; ExPolygons output;
if (holes.empty()) { if (holes.empty()) {
output.reserve(contours.size()); output.reserve(1);
for (ClipperLib::Path &path : contours) if (contours.size() > 1) {
output.emplace_back(std::move(path)); // One expolygon with holes created by closing a C shape. Which is which?
} else { output.push_back({});
ClipperLib::Clipper clipper; ExPolygon &out = output.back();
clipper.AddPaths(contours, ClipperLib::ptSubject, true); out.holes.reserve(contours.size() - 1);
clipper.AddPaths(holes, ClipperLib::ptClip, true); for (ClipperLib::Path &path : contours) {
ClipperLib::PolyTree polytree; if (ClipperLib::Area(path) > 0) {
clipper.Execute(ClipperLib::ctDifference, polytree, ClipperLib::pftNonZero, ClipperLib::pftNonZero); // Only one contour with positive area is expected to be created by an outer offset of an ExPolygon.
output = PolyTreeToExPolygons(std::move(polytree)); assert(out.contour.empty());
} out.contour.points = std::move(path);
} else
return output; out.holes.push_back(Polygon{ std::move(path) });
} }
} else {
// Single contour must be CCW.
ExPolygons variable_offset_inner_ex(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit) assert(contours.size() == 1);
{ assert(ClipperLib::Area(contours.front()) > 0);
#ifndef NDEBUG output.push_back(ExPolygon{ std::move(contours.front()) });
// Verify that the deltas are all non positive. }
for (const std::vector<float>& ds : deltas)
for (float delta : ds)
assert(delta <= 0.);
assert(expoly.holes.size() + 1 == deltas.size());
#endif /* NDEBUG */
// 1) Offset the outer contour.
ClipperLib::Paths contours = fix_after_inner_offset(mittered_offset_path_scaled(expoly.contour.points, deltas.front(), miter_limit), ClipperLib::pftNegative, true);
#ifndef NDEBUG
for (auto &c : contours)
assert(ClipperLib::Area(c) > 0.);
#endif /* NDEBUG */
// 2) Offset the holes one by one, collect the results.
ClipperLib::Paths holes;
holes.reserve(expoly.holes.size());
for (const Polygon& hole : expoly.holes)
append(holes, fix_after_outer_offset(mittered_offset_path_scaled(hole.points, deltas[1 + &hole - expoly.holes.data()], miter_limit), ClipperLib::pftNegative, false));
#ifndef NDEBUG
for (auto &c : holes)
assert(ClipperLib::Area(c) > 0.);
#endif /* NDEBUG */
// 3) Subtract holes from the contours.
ExPolygons output;
if (holes.empty()) {
output.reserve(contours.size());
for (ClipperLib::Path &path : contours)
output.emplace_back(std::move(path));
} else { } else {
//FIXME the difference is not needed as the holes may never intersect with other holes.
ClipperLib::Clipper clipper; ClipperLib::Clipper clipper;
// Contours may have holes if they were created by closing a C shape.
clipper.AddPaths(contours, ClipperLib::ptSubject, true); clipper.AddPaths(contours, ClipperLib::ptSubject, true);
clipper.AddPaths(holes, ClipperLib::ptClip, true); clipper.AddPaths(holes, ClipperLib::ptClip, true);
ClipperLib::PolyTree polytree; ClipperLib::PolyTree polytree;
@ -1339,6 +1398,7 @@ ExPolygons variable_offset_inner_ex(const ExPolygon &expoly, const std::vector<s
output = PolyTreeToExPolygons(std::move(polytree)); output = PolyTreeToExPolygons(std::move(polytree));
} }
assert(output.size() == 1);
return output; return output;
} }

View File

@ -133,21 +133,21 @@ namespace ClipperUtils {
const std::vector<PathType> &m_paths; const std::vector<PathType> &m_paths;
}; };
template<typename MultiPointType> template<typename MultiPointsType>
class MultiPointsProvider { class MultiPointsProvider {
public: public:
MultiPointsProvider(const std::vector<MultiPointType> &multipoints) : m_multipoints(multipoints) {} MultiPointsProvider(const MultiPointsType &multipoints) : m_multipoints(multipoints) {}
struct iterator : public PathsProviderIteratorBase { struct iterator : public PathsProviderIteratorBase {
public: public:
explicit iterator(typename std::vector<MultiPointType>::const_iterator it) : m_it(it) {} explicit iterator(typename MultiPointsType::const_iterator it) : m_it(it) {}
const Points& operator*() const { return m_it->points; } const Points& operator*() const { return m_it->points; }
bool operator==(const iterator &rhs) const { return m_it == rhs.m_it; } bool operator==(const iterator &rhs) const { return m_it == rhs.m_it; }
bool operator!=(const iterator &rhs) const { return !(*this == rhs); } bool operator!=(const iterator &rhs) const { return !(*this == rhs); }
const Points& operator++(int) { return (m_it ++)->points; } const Points& operator++(int) { return (m_it ++)->points; }
iterator& operator++() { ++ m_it; return *this; } iterator& operator++() { ++ m_it; return *this; }
private: private:
typename std::vector<MultiPointType>::const_iterator m_it; typename MultiPointsType::const_iterator m_it;
}; };
iterator cbegin() const { return iterator(m_multipoints.begin()); } iterator cbegin() const { return iterator(m_multipoints.begin()); }
@ -157,11 +157,11 @@ namespace ClipperUtils {
size_t size() const { return m_multipoints.size(); } size_t size() const { return m_multipoints.size(); }
private: private:
const std::vector<MultiPointType> &m_multipoints; const MultiPointsType &m_multipoints;
}; };
using PolygonsProvider = MultiPointsProvider<Polygon>; using PolygonsProvider = MultiPointsProvider<Polygons>;
using PolylinesProvider = MultiPointsProvider<Polyline>; using PolylinesProvider = MultiPointsProvider<Polylines>;
struct ExPolygonProvider { struct ExPolygonProvider {
ExPolygonProvider(const ExPolygon &expoly) : m_expoly(expoly) {} ExPolygonProvider(const ExPolygon &expoly) : m_expoly(expoly) {}

View File

@ -40,7 +40,7 @@ inline ZPath to_zpath(const Points &path, coord_t z)
// Convert multiple paths to paths with a given Z coordinate. // Convert multiple paths to paths with a given Z coordinate.
// If Open, then duplicate the first point of each path at its end. // If Open, then duplicate the first point of each path at its end.
template<bool Open = false> template<bool Open = false>
inline ZPaths to_zpaths(const std::vector<Points> &paths, coord_t z) inline ZPaths to_zpaths(const VecOfPoints &paths, coord_t z)
{ {
ZPaths out; ZPaths out;
out.reserve(paths.size()); out.reserve(paths.size());
@ -86,16 +86,16 @@ inline Points from_zpath(const ZPoints &path)
// Convert multiple paths to paths with a given Z coordinate. // Convert multiple paths to paths with a given Z coordinate.
// If Open, then duplicate the first point of each path at its end. // If Open, then duplicate the first point of each path at its end.
template<bool Open = false> template<bool Open = false>
inline void from_zpaths(const ZPaths &paths, std::vector<Points> &out) inline void from_zpaths(const ZPaths &paths, VecOfPoints &out)
{ {
out.reserve(out.size() + paths.size()); out.reserve(out.size() + paths.size());
for (const ZPoints &path : paths) for (const ZPoints &path : paths)
out.emplace_back(from_zpath<Open>(path)); out.emplace_back(from_zpath<Open>(path));
} }
template<bool Open = false> template<bool Open = false>
inline std::vector<Points> from_zpaths(const ZPaths &paths) inline VecOfPoints from_zpaths(const ZPaths &paths)
{ {
std::vector<Points> out; VecOfPoints out;
from_zpaths(paths, out); from_zpaths(paths, out);
return out; return out;
} }

View File

@ -1991,7 +1991,7 @@ public:
void set_enum_labels(GUIType gui_type, const std::initializer_list<std::string_view> il) { void set_enum_labels(GUIType gui_type, const std::initializer_list<std::string_view> il) {
this->enum_def_new(); this->enum_def_new();
assert(gui_type == GUIType::i_enum_open || gui_type == GUIType::f_enum_open || gui_type == ConfigOptionDef::GUIType::select_open); assert(gui_type == GUIType::i_enum_open || gui_type == GUIType::f_enum_open || gui_type == ConfigOptionDef::GUIType::select_close);
this->gui_type = gui_type; this->gui_type = gui_type;
enum_def->set_labels(il); enum_def->set_labels(il);
} }

View File

@ -17,7 +17,7 @@ public:
Contour() = default; Contour() = default;
Contour(const Slic3r::Point *begin, const Slic3r::Point *end, bool open) : m_begin(begin), m_end(end), m_open(open) {} Contour(const Slic3r::Point *begin, const Slic3r::Point *end, bool open) : m_begin(begin), m_end(end), m_open(open) {}
Contour(const Slic3r::Point *data, size_t size, bool open) : Contour(data, data + size, open) {} Contour(const Slic3r::Point *data, size_t size, bool open) : Contour(data, data + size, open) {}
Contour(const std::vector<Slic3r::Point> &pts, bool open) : Contour(pts.data(), pts.size(), open) {} Contour(const Points &pts, bool open) : Contour(pts.data(), pts.size(), open) {}
const Slic3r::Point *begin() const { return m_begin; } const Slic3r::Point *begin() const { return m_begin; }
const Slic3r::Point *end() const { return m_end; } const Slic3r::Point *end() const { return m_end; }

View File

@ -597,7 +597,8 @@ ExPolygon elephant_foot_compensation(const ExPolygon &input_expoly, double min_c
} }
ExPolygons out_vec = variable_offset_inner_ex(resampled, deltas, 2.); ExPolygons out_vec = variable_offset_inner_ex(resampled, deltas, 2.);
if (out_vec.size() == 1) if (out_vec.size() == 1 && out_vec.front().holes.size() == resampled.holes.size())
// No contour of the original compensated expolygon was lost.
out = std::move(out_vec.front()); out = std::move(out_vec.front());
else { else {
// Something went wrong, don't compensate. // Something went wrong, don't compensate.
@ -610,6 +611,7 @@ ExPolygon elephant_foot_compensation(const ExPolygon &input_expoly, double min_c
{ { out_vec }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } } }); { { out_vec }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } } });
} }
#endif /* TESTS_EXPORT_SVGS */ #endif /* TESTS_EXPORT_SVGS */
// It may be that the source expolygons contained non-manifold vertices, for which the variable offset may not produce the same number of contours or holes.
assert(out_vec.size() == 1); assert(out_vec.size() == 1);
} }
} }

View File

@ -796,8 +796,7 @@ const Glyph* priv::get_glyph(
auto glyph_item = cache.find(unicode); auto glyph_item = cache.find(unicode);
if (glyph_item != cache.end()) return &glyph_item->second; if (glyph_item != cache.end()) return &glyph_item->second;
unsigned int font_index = font_prop.collection_number.has_value()? unsigned int font_index = font_prop.collection_number.value_or(0);
*font_prop.collection_number : 0;
if (!is_valid(font, font_index)) return nullptr; if (!is_valid(font, font_index)) return nullptr;
if (!font_info_opt.has_value()) { if (!font_info_opt.has_value()) {
@ -835,11 +834,10 @@ const Glyph* priv::get_glyph(
glyph_opt->shape = Slic3r::union_ex(offset_ex(glyph_opt->shape, delta)); glyph_opt->shape = Slic3r::union_ex(offset_ex(glyph_opt->shape, delta));
} }
if (font_prop.skew.has_value()) { if (font_prop.skew.has_value()) {
const float &ratio = *font_prop.skew; double ratio = *font_prop.skew;
auto skew = [&ratio](Polygon &polygon) { auto skew = [&ratio](Polygon &polygon) {
for (Slic3r::Point &p : polygon.points) { for (Slic3r::Point &p : polygon.points)
p.x() += p.y() * ratio; p.x() += static_cast<Point::coord_type>(std::round(p.y() * ratio));
}
}; };
for (ExPolygon &expolygon : glyph_opt->shape) { for (ExPolygon &expolygon : glyph_opt->shape) {
skew(expolygon.contour); skew(expolygon.contour);
@ -1363,10 +1361,9 @@ std::string Emboss::create_range_text(const std::string &text,
double Emboss::get_shape_scale(const FontProp &fp, const FontFile &ff) double Emboss::get_shape_scale(const FontProp &fp, const FontFile &ff)
{ {
const auto &cn = fp.collection_number; size_t font_index = fp.collection_number.value_or(0);
unsigned int font_index = (cn.has_value()) ? *cn : 0; const FontFile::Info &info = ff.infos[font_index];
int unit_per_em = ff.infos[font_index].unit_per_em; double scale = fp.size_in_mm / (double) info.unit_per_em;
double scale = fp.size_in_mm / unit_per_em;
// Shape is scaled for store point coordinate as integer // Shape is scaled for store point coordinate as integer
return scale * SHAPE_SCALE; return scale * SHAPE_SCALE;
} }

View File

@ -10,6 +10,8 @@
#include <cassert> #include <cassert>
#include <list> #include <list>
#include <ankerl/unordered_dense.h>
namespace Slic3r { namespace Slic3r {
void ExPolygon::scale(double factor) void ExPolygon::scale(double factor)
@ -395,7 +397,7 @@ bool has_duplicate_points(const ExPolygon &expoly)
size_t cnt = expoly.contour.points.size(); size_t cnt = expoly.contour.points.size();
for (const Polygon &hole : expoly.holes) for (const Polygon &hole : expoly.holes)
cnt += hole.points.size(); cnt += hole.points.size();
std::vector<Point> allpts; Points allpts;
allpts.reserve(cnt); allpts.reserve(cnt);
allpts.insert(allpts.begin(), expoly.contour.points.begin(), expoly.contour.points.end()); allpts.insert(allpts.begin(), expoly.contour.points.begin(), expoly.contour.points.end());
for (const Polygon &hole : expoly.holes) for (const Polygon &hole : expoly.holes)
@ -416,20 +418,36 @@ bool has_duplicate_points(const ExPolygons &expolys)
{ {
#if 1 #if 1
// Check globally. // Check globally.
size_t cnt = 0; #if 0
for (const ExPolygon &expoly : expolys) { // Detect duplicates by sorting with quicksort. It is quite fast, but ankerl::unordered_dense is around 1/4 faster.
cnt += expoly.contour.points.size(); Points allpts;
for (const Polygon &hole : expoly.holes) allpts.reserve(count_points(expolys));
cnt += hole.points.size();
}
std::vector<Point> allpts;
allpts.reserve(cnt);
for (const ExPolygon &expoly : expolys) { for (const ExPolygon &expoly : expolys) {
allpts.insert(allpts.begin(), expoly.contour.points.begin(), expoly.contour.points.end()); allpts.insert(allpts.begin(), expoly.contour.points.begin(), expoly.contour.points.end());
for (const Polygon &hole : expoly.holes) for (const Polygon &hole : expoly.holes)
allpts.insert(allpts.end(), hole.points.begin(), hole.points.end()); allpts.insert(allpts.end(), hole.points.begin(), hole.points.end());
} }
return has_duplicate_points(std::move(allpts)); return has_duplicate_points(std::move(allpts));
#else
// Detect duplicates by inserting into an ankerl::unordered_dense hash set, which is is around 1/4 faster than qsort.
struct PointHash {
uint64_t operator()(const Point &p) const noexcept {
uint64_t h;
static_assert(sizeof(h) == sizeof(p));
memcpy(&h, &p, sizeof(p));
return ankerl::unordered_dense::detail::wyhash::hash(h);
}
};
ankerl::unordered_dense::set<Point, PointHash> allpts;
allpts.reserve(count_points(expolys));
for (const ExPolygon &expoly : expolys)
for (size_t icontour = 0; icontour < expoly.num_contours(); ++ icontour)
for (const Point &pt : expoly.contour_or_hole(icontour).points)
if (! allpts.insert(pt).second)
// Duplicate point was discovered.
return true;
return false;
#endif
#else #else
// Check per contour. // Check per contour.
for (const ExPolygon &expoly : expolys) for (const ExPolygon &expoly : expolys)

View File

@ -867,9 +867,12 @@ namespace Slic3r {
IdToCutObjectInfoMap::iterator cut_object_info = m_cut_object_infos.find(object.second + 1); IdToCutObjectInfoMap::iterator cut_object_info = m_cut_object_infos.find(object.second + 1);
if (cut_object_info != m_cut_object_infos.end()) { if (cut_object_info != m_cut_object_infos.end()) {
model_object->cut_id = cut_object_info->second.id; model_object->cut_id = cut_object_info->second.id;
int vol_cnt = int(model_object->volumes.size());
for (auto connector : cut_object_info->second.connectors) { for (auto connector : cut_object_info->second.connectors) {
assert(0 <= connector.volume_id && connector.volume_id <= int(model_object->volumes.size())); if (connector.volume_id < 0 || connector.volume_id >= vol_cnt) {
add_error("Invalid connector is found");
continue;
}
model_object->volumes[connector.volume_id]->cut_info = model_object->volumes[connector.volume_id]->cut_info =
ModelVolume::CutInfo(CutConnectorType(connector.type), connector.r_tolerance, connector.h_tolerance, true); ModelVolume::CutInfo(CutConnectorType(connector.type), connector.r_tolerance, connector.h_tolerance, true);
} }
@ -2958,9 +2961,9 @@ namespace Slic3r {
unsigned int object_cnt = 0; unsigned int object_cnt = 0;
for (const ModelObject* object : model.objects) { for (const ModelObject* object : model.objects) {
object_cnt++;
if (!object->is_cut()) if (!object->is_cut())
continue; continue;
object_cnt++;
pt::ptree& obj_tree = tree.add("objects.object", ""); pt::ptree& obj_tree = tree.add("objects.object", "");
obj_tree.put("<xmlattr>.id", object_cnt); obj_tree.put("<xmlattr>.id", object_cnt);

View File

@ -1,4 +1,4 @@
#include "pwmx.hpp" #include "AnycubicSLA.hpp"
#include "GCode/ThumbnailData.hpp" #include "GCode/ThumbnailData.hpp"
#include "SLA/RasterBase.hpp" #include "SLA/RasterBase.hpp"
#include "libslic3r/SLAPrint.hpp" #include "libslic3r/SLAPrint.hpp"
@ -22,6 +22,8 @@
#define CFG_DELAY_BEFORE_EXPOSURE "DELAY_BEFORE_EXPOSURE" #define CFG_DELAY_BEFORE_EXPOSURE "DELAY_BEFORE_EXPOSURE"
#define CFG_BOTTOM_LIFT_SPEED "BOTTOM_LIFT_SPEED" #define CFG_BOTTOM_LIFT_SPEED "BOTTOM_LIFT_SPEED"
#define CFG_BOTTOM_LIFT_DISTANCE "BOTTOM_LIFT_DISTANCE" #define CFG_BOTTOM_LIFT_DISTANCE "BOTTOM_LIFT_DISTANCE"
#define CFG_ANTIALIASING "ANTIALIASING"
#define PREV_W 224 #define PREV_W 224
#define PREV_H 168 #define PREV_H 168
@ -31,7 +33,7 @@
namespace Slic3r { namespace Slic3r {
static void pwx_get_pixel_span(const std::uint8_t* ptr, const std::uint8_t* end, static void anycubicsla_get_pixel_span(const std::uint8_t* ptr, const std::uint8_t* end,
std::uint8_t& pixel, size_t& span_len) std::uint8_t& pixel, size_t& span_len)
{ {
size_t max_len; size_t max_len;
@ -46,7 +48,7 @@ static void pwx_get_pixel_span(const std::uint8_t* ptr, const std::uint8_t* end,
} }
} }
struct PWXRasterEncoder struct AnycubicSLARasterEncoder
{ {
sla::EncodedRaster operator()(const void *ptr, sla::EncodedRaster operator()(const void *ptr,
size_t w, size_t w,
@ -62,7 +64,7 @@ struct PWXRasterEncoder
const std::uint8_t *src = reinterpret_cast<const std::uint8_t *>(ptr); const std::uint8_t *src = reinterpret_cast<const std::uint8_t *>(ptr);
const std::uint8_t *src_end = src + size; const std::uint8_t *src_end = src + size;
while (src < src_end) { while (src < src_end) {
pwx_get_pixel_span(src, src_end, pixel, span_len); anycubicsla_get_pixel_span(src, src_end, pixel, span_len);
src += span_len; src += span_len;
// fully transparent of fully opaque pixel // fully transparent of fully opaque pixel
if (pixel == 0 || pixel == 0xF0) { if (pixel == 0 || pixel == 0xF0) {
@ -78,27 +80,27 @@ struct PWXRasterEncoder
} }
} }
return sla::EncodedRaster(std::move(dst), "pwx"); return sla::EncodedRaster(std::move(dst), "pwimg");
} }
}; };
using ConfMap = std::map<std::string, std::string>; using ConfMap = std::map<std::string, std::string>;
typedef struct pwmx_format_intro typedef struct anycubicsla_format_intro
{ {
char tag[12]; char tag[12];
std::uint32_t version; // value 1 std::uint32_t version; // value 1 (also known as 515, 516 and 517)
std::uint32_t area_num; // unknown - usually 4 std::uint32_t area_num; // Number of tables - usually 4
std::uint32_t header_data_offset; std::uint32_t header_data_offset;
std::float_t intro24; // unknown - usually 0 std::uint32_t software_data_offset; // unused in version 1
std::uint32_t preview_data_offset; std::uint32_t preview_data_offset;
std::float_t intro32; // unknown std::uint32_t layer_color_offset; // unused in version 1
std::uint32_t layer_data_offset; std::uint32_t layer_data_offset;
std::float_t intro40; // unknown std::uint32_t extra_data_offset; // unused here (only used in version 516)
std::uint32_t image_data_offset; std::uint32_t image_data_offset;
} pwmx_format_intro; } anycubicsla_format_intro;
typedef struct pwmx_format_header typedef struct anycubicsla_format_header
{ {
char tag[12]; char tag[12];
std::uint32_t payload_size; std::uint32_t payload_size;
@ -121,11 +123,11 @@ typedef struct pwmx_format_header
std::uint32_t per_layer_override; // ? unknown meaning ? std::uint32_t per_layer_override; // ? unknown meaning ?
std::uint32_t print_time_s; std::uint32_t print_time_s;
std::uint32_t transition_layer_count; std::uint32_t transition_layer_count;
std::uint32_t unknown; // ? usually 0 ? std::uint32_t transition_layer_type; // usually 0
} pwmx_format_header; } anycubicsla_format_header;
typedef struct pwmx_format_preview typedef struct anycubicsla_format_preview
{ {
char tag[12]; char tag[12];
std::uint32_t payload_size; std::uint32_t payload_size;
@ -134,16 +136,16 @@ typedef struct pwmx_format_preview
std::uint32_t preview_h; std::uint32_t preview_h;
// raw image data in BGR565 format // raw image data in BGR565 format
std::uint8_t pixels[PREV_W * PREV_H * 2]; std::uint8_t pixels[PREV_W * PREV_H * 2];
} pwmx_format_preview; } anycubicsla_format_preview;
typedef struct pwmx_format_layers_header typedef struct anycubicsla_format_layers_header
{ {
char tag[12]; char tag[12];
std::uint32_t payload_size; std::uint32_t payload_size;
std::uint32_t layer_count; std::uint32_t layer_count;
} pwmx_format_layers_header; } anycubicsla_format_layers_header;
typedef struct pwmx_format_layer typedef struct anycubicsla_format_layer
{ {
std::uint32_t image_offset; std::uint32_t image_offset;
std::uint32_t image_size; std::uint32_t image_size;
@ -153,20 +155,20 @@ typedef struct pwmx_format_layer
std::float_t layer_height_mm; std::float_t layer_height_mm;
std::float_t layer44; // unkown - usually 0 std::float_t layer44; // unkown - usually 0
std::float_t layer48; // unkown - usually 0 std::float_t layer48; // unkown - usually 0
} pwmx_format_layer; } anycubicsla_format_layer;
typedef struct pwmx_format_misc typedef struct anycubicsla_format_misc
{ {
std::float_t bottom_layer_height_mm; std::float_t bottom_layer_height_mm;
std::float_t bottom_lift_distance_mm; std::float_t bottom_lift_distance_mm;
std::float_t bottom_lift_speed_mms; std::float_t bottom_lift_speed_mms;
} pwmx_format_misc; } anycubicsla_format_misc;
class PwmxFormatConfigDef : public ConfigDef class AnycubicSLAFormatConfigDef : public ConfigDef
{ {
public: public:
PwmxFormatConfigDef() AnycubicSLAFormatConfigDef()
{ {
add(CFG_LIFT_DISTANCE, coFloat); add(CFG_LIFT_DISTANCE, coFloat);
add(CFG_LIFT_SPEED, coFloat); add(CFG_LIFT_SPEED, coFloat);
@ -174,17 +176,18 @@ public:
add(CFG_DELAY_BEFORE_EXPOSURE, coFloat); add(CFG_DELAY_BEFORE_EXPOSURE, coFloat);
add(CFG_BOTTOM_LIFT_DISTANCE, coFloat); add(CFG_BOTTOM_LIFT_DISTANCE, coFloat);
add(CFG_BOTTOM_LIFT_SPEED, coFloat); add(CFG_BOTTOM_LIFT_SPEED, coFloat);
add(CFG_ANTIALIASING, coInt);
} }
}; };
class PwmxFormatDynamicConfig : public DynamicConfig class AnycubicSLAFormatDynamicConfig : public DynamicConfig
{ {
public: public:
PwmxFormatDynamicConfig(){}; AnycubicSLAFormatDynamicConfig(){};
const ConfigDef *def() const override { return &config_def; } const ConfigDef *def() const override { return &config_def; }
private: private:
PwmxFormatConfigDef config_def; AnycubicSLAFormatConfigDef config_def;
}; };
namespace { namespace {
@ -222,8 +225,8 @@ template<class T> void crop_value(T &val, T val_min, T val_max)
} }
} }
void fill_preview(pwmx_format_preview &p, void fill_preview(anycubicsla_format_preview &p,
pwmx_format_misc &/*m*/, anycubicsla_format_misc &/*m*/,
const ThumbnailsList &thumbnails) const ThumbnailsList &thumbnails)
{ {
@ -266,9 +269,8 @@ void fill_preview(pwmx_format_preview &p,
} }
} }
void fill_header(anycubicsla_format_header &h,
void fill_header(pwmx_format_header &h, anycubicsla_format_misc &m,
pwmx_format_misc &m,
const SLAPrint &print, const SLAPrint &print,
std::uint32_t layer_count) std::uint32_t layer_count)
{ {
@ -282,7 +284,7 @@ void fill_header(pwmx_format_header &h,
auto mat_opt = cfg.option("material_notes"); auto mat_opt = cfg.option("material_notes");
std::string mnotes = mat_opt? cfg.option("material_notes")->serialize() : ""; std::string mnotes = mat_opt? cfg.option("material_notes")->serialize() : "";
// create a config parser from the material notes // create a config parser from the material notes
Slic3r::PwmxFormatDynamicConfig mat_cfg; Slic3r::AnycubicSLAFormatDynamicConfig mat_cfg;
SLAPrintStatistics stats = print.print_statistics(); SLAPrintStatistics stats = print.print_statistics();
// sanitize the string config // sanitize the string config
@ -314,6 +316,13 @@ void fill_header(pwmx_format_header &h,
h.per_layer_override = 0; h.per_layer_override = 0;
// TODO - expose these variables to the UI rather than using material notes // TODO - expose these variables to the UI rather than using material notes
if (mat_cfg.has(CFG_ANTIALIASING)) {
h.antialiasing = get_cfg_value_i(mat_cfg, CFG_ANTIALIASING);
crop_value(h.antialiasing, (uint32_t) 0, (uint32_t) 1);
} else {
h.antialiasing = 1;
}
h.delay_before_exposure_s = get_cfg_value_f(mat_cfg, CFG_DELAY_BEFORE_EXPOSURE, 0.5f); h.delay_before_exposure_s = get_cfg_value_f(mat_cfg, CFG_DELAY_BEFORE_EXPOSURE, 0.5f);
crop_value(h.delay_before_exposure_s, 0.0f, 1000.0f); crop_value(h.delay_before_exposure_s, 0.0f, 1000.0f);
@ -356,7 +365,7 @@ void fill_header(pwmx_format_header &h,
} // namespace } // namespace
std::unique_ptr<sla::RasterBase> PwmxArchive::create_raster() const std::unique_ptr<sla::RasterBase> AnycubicSLAArchive::create_raster() const
{ {
sla::Resolution res; sla::Resolution res;
sla::PixelDim pxdim; sla::PixelDim pxdim;
@ -389,13 +398,13 @@ std::unique_ptr<sla::RasterBase> PwmxArchive::create_raster() const
return sla::create_raster_grayscale_aa(res, pxdim, gamma, tr); return sla::create_raster_grayscale_aa(res, pxdim, gamma, tr);
} }
sla::RasterEncoder PwmxArchive::get_encoder() const sla::RasterEncoder AnycubicSLAArchive::get_encoder() const
{ {
return PWXRasterEncoder{}; return AnycubicSLARasterEncoder{};
} }
// Endian safe write of little endian 32bit ints // Endian safe write of little endian 32bit ints
static void pwmx_write_int32(std::ofstream &out, std::uint32_t val) static void anycubicsla_write_int32(std::ofstream &out, std::uint32_t val)
{ {
const char i1 = (val & 0xFF); const char i1 = (val & 0xFF);
const char i2 = (val >> 8) & 0xFF; const char i2 = (val >> 8) & 0xFF;
@ -407,104 +416,106 @@ static void pwmx_write_int32(std::ofstream &out, std::uint32_t val)
out.write((const char *) &i3, 1); out.write((const char *) &i3, 1);
out.write((const char *) &i4, 1); out.write((const char *) &i4, 1);
} }
static void pwmx_write_float(std::ofstream &out, std::float_t val) static void anycubicsla_write_float(std::ofstream &out, std::float_t val)
{ {
std::uint32_t *f = (std::uint32_t *) &val; std::uint32_t *f = (std::uint32_t *) &val;
pwmx_write_int32(out, *f); anycubicsla_write_int32(out, *f);
} }
static void pwmx_write_intro(std::ofstream &out, pwmx_format_intro &i) static void anycubicsla_write_intro(std::ofstream &out, anycubicsla_format_intro &i)
{ {
out.write(TAG_INTRO, sizeof(i.tag)); out.write(TAG_INTRO, sizeof(i.tag));
pwmx_write_int32(out, i.version); anycubicsla_write_int32(out, i.version);
pwmx_write_int32(out, i.area_num); anycubicsla_write_int32(out, i.area_num);
pwmx_write_int32(out, i.header_data_offset); anycubicsla_write_int32(out, i.header_data_offset);
pwmx_write_int32(out, i.intro24); anycubicsla_write_int32(out, i.software_data_offset);
pwmx_write_int32(out, i.preview_data_offset); anycubicsla_write_int32(out, i.preview_data_offset);
pwmx_write_int32(out, i.intro32); anycubicsla_write_int32(out, i.layer_color_offset);
pwmx_write_int32(out, i.layer_data_offset); anycubicsla_write_int32(out, i.layer_data_offset);
pwmx_write_int32(out, i.intro40); anycubicsla_write_int32(out, i.extra_data_offset);
pwmx_write_int32(out, i.image_data_offset); anycubicsla_write_int32(out, i.image_data_offset);
} }
static void pwmx_write_header(std::ofstream &out, pwmx_format_header &h) static void anycubicsla_write_header(std::ofstream &out, anycubicsla_format_header &h)
{ {
out.write(TAG_HEADER, sizeof(h.tag)); out.write(TAG_HEADER, sizeof(h.tag));
pwmx_write_int32(out, h.payload_size); anycubicsla_write_int32(out, h.payload_size);
pwmx_write_float(out, h.pixel_size_um); anycubicsla_write_float(out, h.pixel_size_um);
pwmx_write_float(out, h.layer_height_mm); anycubicsla_write_float(out, h.layer_height_mm);
pwmx_write_float(out, h.exposure_time_s); anycubicsla_write_float(out, h.exposure_time_s);
pwmx_write_float(out, h.delay_before_exposure_s); anycubicsla_write_float(out, h.delay_before_exposure_s);
pwmx_write_float(out, h.bottom_exposure_time_s); anycubicsla_write_float(out, h.bottom_exposure_time_s);
pwmx_write_float(out, h.bottom_layer_count); anycubicsla_write_float(out, h.bottom_layer_count);
pwmx_write_float(out, h.lift_distance_mm); anycubicsla_write_float(out, h.lift_distance_mm);
pwmx_write_float(out, h.lift_speed_mms); anycubicsla_write_float(out, h.lift_speed_mms);
pwmx_write_float(out, h.retract_speed_mms); anycubicsla_write_float(out, h.retract_speed_mms);
pwmx_write_float(out, h.volume_ml); anycubicsla_write_float(out, h.volume_ml);
pwmx_write_int32(out, h.antialiasing); anycubicsla_write_int32(out, h.antialiasing);
pwmx_write_int32(out, h.res_x); anycubicsla_write_int32(out, h.res_x);
pwmx_write_int32(out, h.res_y); anycubicsla_write_int32(out, h.res_y);
pwmx_write_float(out, h.weight_g); anycubicsla_write_float(out, h.weight_g);
pwmx_write_float(out, h.price); anycubicsla_write_float(out, h.price);
pwmx_write_int32(out, h.price_currency); anycubicsla_write_int32(out, h.price_currency);
pwmx_write_int32(out, h.per_layer_override); anycubicsla_write_int32(out, h.per_layer_override);
pwmx_write_int32(out, h.print_time_s); anycubicsla_write_int32(out, h.print_time_s);
pwmx_write_int32(out, h.transition_layer_count); anycubicsla_write_int32(out, h.transition_layer_count);
pwmx_write_int32(out, h.unknown); anycubicsla_write_int32(out, h.transition_layer_type);
} }
static void pwmx_write_preview(std::ofstream &out, pwmx_format_preview &p) static void anycubicsla_write_preview(std::ofstream &out, anycubicsla_format_preview &p)
{ {
out.write(TAG_PREVIEW, sizeof(p.tag)); out.write(TAG_PREVIEW, sizeof(p.tag));
pwmx_write_int32(out, p.payload_size); anycubicsla_write_int32(out, p.payload_size);
pwmx_write_int32(out, p.preview_w); anycubicsla_write_int32(out, p.preview_w);
pwmx_write_int32(out, p.preview_dpi); anycubicsla_write_int32(out, p.preview_dpi);
pwmx_write_int32(out, p.preview_h); anycubicsla_write_int32(out, p.preview_h);
out.write((const char*) p.pixels, sizeof(p.pixels)); out.write((const char*) p.pixels, sizeof(p.pixels));
} }
static void pwmx_write_layers_header(std::ofstream &out, pwmx_format_layers_header &h) static void anycubicsla_write_layers_header(std::ofstream &out, anycubicsla_format_layers_header &h)
{ {
out.write(TAG_LAYERS, sizeof(h.tag)); out.write(TAG_LAYERS, sizeof(h.tag));
pwmx_write_int32(out, h.payload_size); anycubicsla_write_int32(out, h.payload_size);
pwmx_write_int32(out, h.layer_count); anycubicsla_write_int32(out, h.layer_count);
} }
static void pwmx_write_layer(std::ofstream &out, pwmx_format_layer &l) static void anycubicsla_write_layer(std::ofstream &out, anycubicsla_format_layer &l)
{ {
pwmx_write_int32(out, l.image_offset); anycubicsla_write_int32(out, l.image_offset);
pwmx_write_int32(out, l.image_size); anycubicsla_write_int32(out, l.image_size);
pwmx_write_float(out, l.lift_distance_mm); anycubicsla_write_float(out, l.lift_distance_mm);
pwmx_write_float(out, l.lift_speed_mms); anycubicsla_write_float(out, l.lift_speed_mms);
pwmx_write_float(out, l.exposure_time_s); anycubicsla_write_float(out, l.exposure_time_s);
pwmx_write_float(out, l.layer_height_mm); anycubicsla_write_float(out, l.layer_height_mm);
pwmx_write_float(out, l.layer44); anycubicsla_write_float(out, l.layer44);
pwmx_write_float(out, l.layer48); anycubicsla_write_float(out, l.layer48);
} }
void PwmxArchive::export_print(const std::string fname, void AnycubicSLAArchive::export_print(const std::string fname,
const SLAPrint &print, const SLAPrint &print,
const ThumbnailsList &thumbnails, const ThumbnailsList &thumbnails,
const std::string &/*projectname*/) const std::string &/*projectname*/)
{ {
std::uint32_t layer_count = m_layers.size(); std::uint32_t layer_count = m_layers.size();
pwmx_format_intro intro = {}; anycubicsla_format_intro intro = {};
pwmx_format_header header = {}; anycubicsla_format_header header = {};
pwmx_format_preview preview = {}; anycubicsla_format_preview preview = {};
pwmx_format_layers_header layers_header = {}; anycubicsla_format_layers_header layers_header = {};
pwmx_format_misc misc = {}; anycubicsla_format_misc misc = {};
std::vector<uint8_t> layer_images; std::vector<uint8_t> layer_images;
std::uint32_t image_offset; std::uint32_t image_offset;
intro.version = 1; assert(m_version == ANYCUBIC_SLA_FORMAT_VERSION_1);
intro.version = m_version;
intro.area_num = 4; intro.area_num = 4;
intro.header_data_offset = sizeof(intro); intro.header_data_offset = sizeof(intro);
intro.preview_data_offset = sizeof(intro) + sizeof(header); intro.preview_data_offset = sizeof(intro) + sizeof(header);
intro.layer_data_offset = intro.preview_data_offset + sizeof(preview); intro.layer_data_offset = intro.preview_data_offset + sizeof(preview);
intro.image_data_offset = intro.layer_data_offset + intro.image_data_offset = intro.layer_data_offset +
sizeof(layers_header) + sizeof(layers_header) +
(sizeof(pwmx_format_layer) * layer_count); (sizeof(anycubicsla_format_layer) * layer_count);
fill_header(header, misc, print, layer_count); fill_header(header, misc, print, layer_count);
fill_preview(preview, misc, thumbnails); fill_preview(preview, misc, thumbnails);
@ -513,21 +524,21 @@ void PwmxArchive::export_print(const std::string fname,
// open the file and write the contents // open the file and write the contents
std::ofstream out; std::ofstream out;
out.open(fname, std::ios::binary | std::ios::out | std::ios::trunc); out.open(fname, std::ios::binary | std::ios::out | std::ios::trunc);
pwmx_write_intro(out, intro); anycubicsla_write_intro(out, intro);
pwmx_write_header(out, header); anycubicsla_write_header(out, header);
pwmx_write_preview(out, preview); anycubicsla_write_preview(out, preview);
layers_header.payload_size = intro.image_data_offset - intro.layer_data_offset - layers_header.payload_size = intro.image_data_offset - intro.layer_data_offset -
sizeof(layers_header.tag) - sizeof(layers_header.payload_size); sizeof(layers_header.tag) - sizeof(layers_header.payload_size);
layers_header.layer_count = layer_count; layers_header.layer_count = layer_count;
pwmx_write_layers_header(out, layers_header); anycubicsla_write_layers_header(out, layers_header);
//layers //layers
layer_images.reserve(layer_count * LAYER_SIZE_ESTIMATE); layer_images.reserve(layer_count * LAYER_SIZE_ESTIMATE);
image_offset = intro.image_data_offset; image_offset = intro.image_data_offset;
size_t i = 0; size_t i = 0;
for (const sla::EncodedRaster &rst : m_layers) { for (const sla::EncodedRaster &rst : m_layers) {
pwmx_format_layer l; anycubicsla_format_layer l;
std::memset(&l, 0, sizeof(l)); std::memset(&l, 0, sizeof(l));
l.image_offset = image_offset; l.image_offset = image_offset;
l.image_size = rst.size(); l.image_size = rst.size();
@ -543,7 +554,7 @@ void PwmxArchive::export_print(const std::string fname,
l.lift_speed_mms = header.lift_speed_mms; l.lift_speed_mms = header.lift_speed_mms;
} }
image_offset += l.image_size; image_offset += l.image_size;
pwmx_write_layer(out, l); anycubicsla_write_layer(out, l);
// add the rle encoded layer image into the buffer // add the rle encoded layer image into the buffer
const char* img_start = reinterpret_cast<const char*>(rst.data()); const char* img_start = reinterpret_cast<const char*>(rst.data());
const char* img_end = img_start + rst.size(); const char* img_end = img_start + rst.size();

View File

@ -0,0 +1,81 @@
#ifndef _SLIC3R_FORMAT_PWMX_HPP_
#define _SLIC3R_FORMAT_PWMX_HPP_
#include <string>
#include "SLAArchiveWriter.hpp"
#include "libslic3r/PrintConfig.hpp"
#define ANYCUBIC_SLA_FORMAT_VERSION_1 1
#define ANYCUBIC_SLA_FORMAT_VERSION_515 515
#define ANYCUBIC_SLA_FORMAT_VERSION_516 516
#define ANYCUBIC_SLA_FORMAT_VERSION_517 517
#define ANYCUBIC_SLA_FORMAT_VERSIONED(FILEFORMAT, NAME, VERSION) \
{ FILEFORMAT, { FILEFORMAT, [] (const auto &cfg) { return std::make_unique<AnycubicSLAArchive>(cfg, VERSION); } } }
#define ANYCUBIC_SLA_FORMAT(FILEFORMAT, NAME) \
ANYCUBIC_SLA_FORMAT_VERSIONED(FILEFORMAT, NAME, ANYCUBIC_SLA_FORMAT_VERSION_1)
/**
// Supports only ANYCUBIC_SLA_VERSION_1
ANYCUBIC_SLA_FORMAT_VERSIONED("pws", "Photon / Photon S", ANYCUBIC_SLA_VERSION_1),
ANYCUBIC_SLA_FORMAT_VERSIONED("pw0", "Photon Zero", ANYCUBIC_SLA_VERSION_1),
ANYCUBIC_SLA_FORMAT_VERSIONED("pwx", "Photon X", ANYCUBIC_SLA_VERSION_1),
// Supports ANYCUBIC_SLA_VERSION_1 and ANYCUBIC_SLA_VERSION_515
ANYCUBIC_SLA_FORMAT_VERSIONED("pwmo", "Photon Mono", ANYCUBIC_SLA_VERSION_1),
ANYCUBIC_SLA_FORMAT_VERSIONED("pwms", "Photon Mono SE", ANYCUBIC_SLA_VERSION_1),
ANYCUBIC_SLA_FORMAT_VERSIONED("dlp", "Photon Ultra", ANYCUBIC_SLA_VERSION_1),
ANYCUBIC_SLA_FORMAT_VERSIONED("pwmx", "Photon Mono X", ANYCUBIC_SLA_VERSION_1),
ANYCUBIC_SLA_FORMAT_VERSIONED("pmsq", "Photon Mono SQ", ANYCUBIC_SLA_VERSION_1),
// Supports ANYCUBIC_SLA_VERSION_515 and ANYCUBIC_SLA_VERSION_516
ANYCUBIC_SLA_FORMAT_VERSIONED("pwma", "Photon Mono 4K", ANYCUBIC_SLA_VERSION_515),
ANYCUBIC_SLA_FORMAT_VERSIONED("pm3", "Photon M3", ANYCUBIC_SLA_VERSION_515),
ANYCUBIC_SLA_FORMAT_VERSIONED("pm3m", "Photon M3 Max", ANYCUBIC_SLA_VERSION_515),
// Supports NYCUBIC_SLA_VERSION_515 and ANYCUBIC_SLA_VERSION_516 and ANYCUBIC_SLA_VERSION_517
ANYCUBIC_SLA_FORMAT_VERSIONED("pwmb", "Photon Mono X 6K / Photon M3 Plus", ANYCUBIC_SLA_VERSION_515),
ANYCUBIC_SLA_FORMAT_VERSIONED("dl2p", "Photon Photon D2", ANYCUBIC_SLA_VERSION_515),
ANYCUBIC_SLA_FORMAT_VERSIONED("pmx2", "Photon Mono X2", ANYCUBIC_SLA_VERSION_515),
ANYCUBIC_SLA_FORMAT_VERSIONED("pm3r", "Photon M3 Premium", ANYCUBIC_SLA_VERSION_515),
*/
namespace Slic3r {
class AnycubicSLAArchive: public SLAArchiveWriter {
SLAPrinterConfig m_cfg;
uint16_t m_version;
protected:
std::unique_ptr<sla::RasterBase> create_raster() const override;
sla::RasterEncoder get_encoder() const override;
SLAPrinterConfig & cfg() { return m_cfg; }
const SLAPrinterConfig & cfg() const { return m_cfg; }
public:
AnycubicSLAArchive() = default;
explicit AnycubicSLAArchive(const SLAPrinterConfig &cfg):
m_cfg(cfg), m_version(ANYCUBIC_SLA_FORMAT_VERSION_1) {}
explicit AnycubicSLAArchive(SLAPrinterConfig &&cfg):
m_cfg(std::move(cfg)), m_version(ANYCUBIC_SLA_FORMAT_VERSION_1) {}
explicit AnycubicSLAArchive(const SLAPrinterConfig &cfg, uint16_t version):
m_cfg(cfg), m_version(version) {}
explicit AnycubicSLAArchive(SLAPrinterConfig &&cfg, uint16_t version):
m_cfg(std::move(cfg)), m_version(version) {}
void export_print(const std::string fname,
const SLAPrint &print,
const ThumbnailsList &thumbnails,
const std::string &projectname = "") override;
};
} // namespace Slic3r::sla
#endif // _SLIC3R_FORMAT_PWMX_HPP_

View File

@ -2,7 +2,7 @@
#include "SL1.hpp" #include "SL1.hpp"
#include "SL1_SVG.hpp" #include "SL1_SVG.hpp"
#include "pwmx.hpp" #include "AnycubicSLA.hpp"
#include "libslic3r/libslic3r.h" #include "libslic3r/libslic3r.h"
@ -33,10 +33,9 @@ static const std::map<std::string, ArchiveEntry> REGISTERED_ARCHIVES {
"SL2", "SL2",
{ "sl1_svg", [] (const auto &cfg) { return std::make_unique<SL1_SVGArchive>(cfg); } } { "sl1_svg", [] (const auto &cfg) { return std::make_unique<SL1_SVGArchive>(cfg); } }
}, },
{ ANYCUBIC_SLA_FORMAT("pwmo", "Photon Mono"),
"pwmx", ANYCUBIC_SLA_FORMAT("pwmx", "Photon Mono X"),
{ "pwmx", [] (const auto &cfg) { return std::make_unique<PwmxArchive>(cfg); } } ANYCUBIC_SLA_FORMAT("pwms", "Photon Mono SE"),
}
}; };
std::unique_ptr<SLAArchiveWriter> std::unique_ptr<SLAArchiveWriter>

View File

@ -1,37 +0,0 @@
#ifndef _SLIC3R_FORMAT_PWMX_HPP_
#define _SLIC3R_FORMAT_PWMX_HPP_
#include <string>
#include "SLAArchiveWriter.hpp"
#include "libslic3r/PrintConfig.hpp"
namespace Slic3r {
class PwmxArchive: public SLAArchiveWriter {
SLAPrinterConfig m_cfg;
protected:
std::unique_ptr<sla::RasterBase> create_raster() const override;
sla::RasterEncoder get_encoder() const override;
SLAPrinterConfig & cfg() { return m_cfg; }
const SLAPrinterConfig & cfg() const { return m_cfg; }
public:
PwmxArchive() = default;
explicit PwmxArchive(const SLAPrinterConfig &cfg): m_cfg(cfg) {}
explicit PwmxArchive(SLAPrinterConfig &&cfg): m_cfg(std::move(cfg)) {}
void export_print(const std::string fname,
const SLAPrint &print,
const ThumbnailsList &thumbnails,
const std::string &projectname = "") override;
};
} // namespace Slic3r::sla
#endif // _SLIC3R_FORMAT_PWMX_HPP_

View File

@ -836,6 +836,7 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
path_tmp += ".tmp"; path_tmp += ".tmp";
m_processor.initialize(path_tmp); m_processor.initialize(path_tmp);
m_processor.set_print(print);
GCodeOutputStream file(boost::nowide::fopen(path_tmp.c_str(), "wb"), m_processor); GCodeOutputStream file(boost::nowide::fopen(path_tmp.c_str(), "wb"), m_processor);
if (! file.is_open()) if (! file.is_open())
throw Slic3r::RuntimeError(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n"); throw Slic3r::RuntimeError(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n");
@ -2354,11 +2355,10 @@ void GCode::process_layer_single_object(
// Round 1 (wiping into object or infill) or round 2 (normal extrusions). // Round 1 (wiping into object or infill) or round 2 (normal extrusions).
const bool print_wipe_extrusions) const bool print_wipe_extrusions)
{ {
//FIXME what the heck ID is this? Layer ID or Object ID? More likely an Object ID. bool first = true;
uint32_t layer_id = 0; int object_id = 0;
bool first = true;
// Delay layer initialization as many layers may not print with all extruders. // Delay layer initialization as many layers may not print with all extruders.
auto init_layer_delayed = [this, &print_instance, &layer_to_print, layer_id, &first, &gcode]() { auto init_layer_delayed = [this, &print_instance, &layer_to_print, &first, &object_id, &gcode]() {
if (first) { if (first) {
first = false; first = false;
const PrintObject &print_object = print_instance.print_object; const PrintObject &print_object = print_instance.print_object;
@ -2374,8 +2374,14 @@ void GCode::process_layer_single_object(
m_avoid_crossing_perimeters.use_external_mp_once(); m_avoid_crossing_perimeters.use_external_mp_once();
m_last_obj_copy = this_object_copy; m_last_obj_copy = this_object_copy;
this->set_origin(unscale(offset)); this->set_origin(unscale(offset));
if (this->config().gcode_label_objects) if (this->config().gcode_label_objects) {
gcode += std::string("; printing object ") + print_object.model_object()->name + " id:" + std::to_string(layer_id) + " copy " + std::to_string(print_instance.instance_id) + "\n"; for (const PrintObject *po : print_object.print()->objects())
if (po == &print_object)
break;
else
++ object_id;
gcode += std::string("; printing object ") + print_object.model_object()->name + " id:" + std::to_string(object_id) + " copy " + std::to_string(print_instance.instance_id) + "\n";
}
} }
}; };
@ -2548,7 +2554,7 @@ void GCode::process_layer_single_object(
} }
} }
if (! first && this->config().gcode_label_objects) if (! first && this->config().gcode_label_objects)
gcode += std::string("; stop printing object ") + print_object.model_object()->name + " id:" + std::to_string(layer_id) + " copy " + std::to_string(print_instance.instance_id) + "\n"; gcode += std::string("; stop printing object ") + print_object.model_object()->name + " id:" + std::to_string(object_id) + " copy " + std::to_string(print_instance.instance_id) + "\n";
} }
void GCode::apply_print_config(const PrintConfig &print_config) void GCode::apply_print_config(const PrintConfig &print_config)
@ -3020,10 +3026,17 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de
{100, ConfigOptionInts{0}}}; {100, ConfigOptionInts{0}}};
} }
double external_perim_reference_speed = std::min(m_config.get_abs_value("external_perimeter_speed"), double external_perim_reference_speed = m_config.get_abs_value("external_perimeter_speed");
std::min(EXTRUDER_CONFIG(filament_max_volumetric_speed) / path.mm3_per_mm, if (external_perim_reference_speed == 0)
m_config.max_volumetric_speed.value / path.mm3_per_mm)); external_perim_reference_speed = m_volumetric_speed / path.mm3_per_mm;
new_points = m_extrusion_quality_estimator.estimate_extrusion_quality(path, overhangs_with_speeds, overhang_w_fan_speeds, if (m_config.max_volumetric_speed.value > 0)
external_perim_reference_speed = std::min(external_perim_reference_speed, m_config.max_volumetric_speed.value / path.mm3_per_mm);
if (EXTRUDER_CONFIG(filament_max_volumetric_speed) > 0) {
external_perim_reference_speed = std::min(external_perim_reference_speed,
EXTRUDER_CONFIG(filament_max_volumetric_speed) / path.mm3_per_mm);
}
new_points = m_extrusion_quality_estimator.estimate_speed_from_extrusion_quality(path, overhangs_with_speeds, overhang_w_fan_speeds,
m_writer.extruder()->id(), external_perim_reference_speed, m_writer.extruder()->id(), external_perim_reference_speed,
speed); speed);
variable_speed_or_fan_speed = std::any_of(new_points.begin(), new_points.end(), variable_speed_or_fan_speed = std::any_of(new_points.begin(), new_points.end(),
@ -3083,8 +3096,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de
std::string cooling_marker_setspeed_comments; std::string cooling_marker_setspeed_comments;
if (m_enable_cooling_markers) { if (m_enable_cooling_markers) {
if (path.role().is_bridge() && if (path.role().is_bridge())
(!path.role().is_perimeter() || !this->config().enable_dynamic_fan_speeds.get_at(m_writer.extruder()->id())))
gcode += ";_BRIDGE_FAN_START\n"; gcode += ";_BRIDGE_FAN_START\n";
else else
cooling_marker_setspeed_comments = ";_EXTRUDE_SET_SPEED"; cooling_marker_setspeed_comments = ";_EXTRUDE_SET_SPEED";
@ -3120,7 +3132,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de
double last_set_speed = new_points[0].speed * 60.0; double last_set_speed = new_points[0].speed * 60.0;
double last_set_fan_speed = new_points[0].fan_speed; double last_set_fan_speed = new_points[0].fan_speed;
gcode += m_writer.set_speed(last_set_speed, "", cooling_marker_setspeed_comments); gcode += m_writer.set_speed(last_set_speed, "", cooling_marker_setspeed_comments);
gcode += ";_SET_FAN_SPEED" + std::to_string(int(last_set_fan_speed)) + "\n"; gcode += "\n;_SET_FAN_SPEED" + std::to_string(int(last_set_fan_speed)) + "\n";
Vec2d prev = this->point_to_gcode_quantized(new_points[0].p); Vec2d prev = this->point_to_gcode_quantized(new_points[0].p);
for (size_t i = 1; i < new_points.size(); i++) { for (size_t i = 1; i < new_points.size(); i++) {
const ProcessedPoint &processed_point = new_points[i]; const ProcessedPoint &processed_point = new_points[i];
@ -3135,10 +3147,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de
} }
if (last_set_fan_speed != processed_point.fan_speed) { if (last_set_fan_speed != processed_point.fan_speed) {
last_set_fan_speed = processed_point.fan_speed; last_set_fan_speed = processed_point.fan_speed;
gcode += ";_SET_FAN_SPEED" + std::to_string(int(last_set_fan_speed)) + "\n"; gcode += "\n;_SET_FAN_SPEED" + std::to_string(int(last_set_fan_speed)) + "\n";
} }
} }
gcode += ";_RESET_FAN_SPEED\n"; gcode += "\n;_RESET_FAN_SPEED\n";
} }
if (m_enable_cooling_markers) if (m_enable_cooling_markers)

View File

@ -13,6 +13,7 @@
#include "../ClipperUtils.hpp" #include "../ClipperUtils.hpp"
#include "../Flow.hpp" #include "../Flow.hpp"
#include "../Config.hpp" #include "../Config.hpp"
#include "../Line.hpp"
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
@ -20,107 +21,32 @@
#include <iterator> #include <iterator>
#include <limits> #include <limits>
#include <numeric> #include <numeric>
#include <ostream>
#include <unordered_map> #include <unordered_map>
#include <utility> #include <utility>
#include <vector> #include <vector>
namespace Slic3r { namespace Slic3r {
class SlidingWindowCurvatureAccumulator
{
float window_size;
float total_distance = 0; // accumulated distance
float total_curvature = 0; // accumulated signed ccw angles
deque<float> distances;
deque<float> angles;
public:
SlidingWindowCurvatureAccumulator(float window_size) : window_size(window_size) {}
void add_point(float distance, float angle)
{
total_distance += distance;
total_curvature += angle;
distances.push_back(distance);
angles.push_back(angle);
while (distances.size() > 1 && total_distance > window_size) {
total_distance -= distances.front();
total_curvature -= angles.front();
distances.pop_front();
angles.pop_front();
}
}
float get_curvature() const
{
return total_curvature / window_size;
}
void reset()
{
total_curvature = 0;
total_distance = 0;
distances.clear();
angles.clear();
}
};
class CurvatureEstimator
{
static const size_t sliders_count = 3;
SlidingWindowCurvatureAccumulator sliders[sliders_count] = {{1.0},{4.0}, {10.0}};
public:
void add_point(float distance, float angle)
{
if (distance < EPSILON)
return;
for (SlidingWindowCurvatureAccumulator &slider : sliders) {
slider.add_point(distance, angle);
}
}
float get_curvature()
{
float max_curvature = 0.0f;
for (const SlidingWindowCurvatureAccumulator &slider : sliders) {
if (abs(slider.get_curvature()) > abs(max_curvature)) {
max_curvature = slider.get_curvature();
}
}
return max_curvature;
}
void reset()
{
for (SlidingWindowCurvatureAccumulator &slider : sliders) {
slider.reset();
}
}
};
struct ExtendedPoint struct ExtendedPoint
{ {
ExtendedPoint(Vec2d position, float distance = 0.0, size_t nearest_prev_layer_line = size_t(-1), float curvature = 0.0)
: position(position), distance(distance), nearest_prev_layer_line(nearest_prev_layer_line), curvature(curvature)
{}
Vec2d position; Vec2d position;
float distance; float distance;
size_t nearest_prev_layer_line;
float curvature; float curvature;
}; };
template<bool SCALED_INPUT, bool ADD_INTERSECTIONS, bool PREV_LAYER_BOUNDARY_OFFSET, bool SIGNED_DISTANCE, typename P, typename L> template<bool SCALED_INPUT, bool ADD_INTERSECTIONS, bool PREV_LAYER_BOUNDARY_OFFSET, bool SIGNED_DISTANCE, typename POINTS, typename L>
std::vector<ExtendedPoint> estimate_points_properties(const std::vector<P> &input_points, std::vector<ExtendedPoint> estimate_points_properties(const POINTS &input_points,
const AABBTreeLines::LinesDistancer<L> &unscaled_prev_layer, const AABBTreeLines::LinesDistancer<L> &unscaled_prev_layer,
float flow_width, float flow_width,
float max_line_length = -1.0f) float max_line_length = -1.0f)
{ {
using P = typename POINTS::value_type;
using AABBScalar = typename AABBTreeLines::LinesDistancer<L>::Scalar; using AABBScalar = typename AABBTreeLines::LinesDistancer<L>::Scalar;
if (input_points.empty()) if (input_points.empty())
return {}; return {};
float boundary_offset = PREV_LAYER_BOUNDARY_OFFSET ? 0.5 * flow_width : 0.0f; float boundary_offset = PREV_LAYER_BOUNDARY_OFFSET ? 0.5 * flow_width : 0.0f;
CurvatureEstimator cestim;
auto maybe_unscale = [](const P &p) { return SCALED_INPUT ? unscaled(p) : p.template cast<double>(); }; auto maybe_unscale = [](const P &p) { return SCALED_INPUT ? unscaled(p) : p.template cast<double>(); };
std::vector<ExtendedPoint> points; std::vector<ExtendedPoint> points;
@ -130,21 +56,22 @@ std::vector<ExtendedPoint> estimate_points_properties(const std::vector<P>
ExtendedPoint start_point{maybe_unscale(input_points.front())}; ExtendedPoint start_point{maybe_unscale(input_points.front())};
auto [distance, nearest_line, x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(start_point.position.cast<AABBScalar>()); auto [distance, nearest_line, x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(start_point.position.cast<AABBScalar>());
start_point.distance = distance + boundary_offset; start_point.distance = distance + boundary_offset;
start_point.nearest_prev_layer_line = nearest_line;
points.push_back(start_point); points.push_back(start_point);
} }
for (size_t i = 1; i < input_points.size(); i++) { for (size_t i = 1; i < input_points.size(); i++) {
ExtendedPoint next_point{maybe_unscale(input_points[i])}; ExtendedPoint next_point{maybe_unscale(input_points[i])};
auto [distance, nearest_line, x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(next_point.position.cast<AABBScalar>()); auto [distance, nearest_line, x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(next_point.position.cast<AABBScalar>());
next_point.distance = distance + boundary_offset; next_point.distance = distance + boundary_offset;
next_point.nearest_prev_layer_line = nearest_line;
if (ADD_INTERSECTIONS && if (ADD_INTERSECTIONS &&
((points.back().distance > boundary_offset + EPSILON) != (next_point.distance > boundary_offset + EPSILON))) { ((points.back().distance > boundary_offset + EPSILON) != (next_point.distance > boundary_offset + EPSILON))) {
const ExtendedPoint &prev_point = points.back(); const ExtendedPoint &prev_point = points.back();
auto intersections = unscaled_prev_layer.template intersections_with_line<true>(L{prev_point.position.cast<AABBScalar>(), next_point.position.cast<AABBScalar>()}); auto intersections = unscaled_prev_layer.template intersections_with_line<true>(L{prev_point.position.cast<AABBScalar>(), next_point.position.cast<AABBScalar>()});
for (const auto &intersection : intersections) { for (const auto &intersection : intersections) {
points.emplace_back(intersection.first.template cast<double>(), boundary_offset, intersection.second); ExtendedPoint p{};
p.position = intersection.first.template cast<double>();
p.distance = boundary_offset;
points.push_back(p);
} }
} }
points.push_back(next_point); points.push_back(next_point);
@ -170,12 +97,18 @@ std::vector<ExtendedPoint> estimate_points_properties(const std::vector<P>
if (t0 < 1.0) { if (t0 < 1.0) {
auto p0 = curr.position + t0 * (next.position - curr.position); auto p0 = curr.position + t0 * (next.position - curr.position);
auto [p0_dist, p0_near_l, p0_x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(p0.cast<AABBScalar>()); auto [p0_dist, p0_near_l, p0_x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(p0.cast<AABBScalar>());
new_points.push_back(ExtendedPoint{p0, float(p0_dist + boundary_offset), p0_near_l}); ExtendedPoint new_p{};
new_p.position = p0;
new_p.distance = float(p0_dist + boundary_offset);
new_points.push_back(new_p);
} }
if (t1 > 0.0) { if (t1 > 0.0) {
auto p1 = curr.position + t1 * (next.position - curr.position); auto p1 = curr.position + t1 * (next.position - curr.position);
auto [p1_dist, p1_near_l, p1_x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(p1.cast<AABBScalar>()); auto [p1_dist, p1_near_l, p1_x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(p1.cast<AABBScalar>());
new_points.push_back(ExtendedPoint{p1, float(p1_dist + boundary_offset), p1_near_l}); ExtendedPoint new_p{};
new_p.position = p1;
new_p.distance = float(p1_dist + boundary_offset);
new_points.push_back(new_p);
} }
} }
} }
@ -199,7 +132,10 @@ std::vector<ExtendedPoint> estimate_points_properties(const std::vector<P>
Vec2d pos = curr.position * (1.0 - j * t) + next.position * (j * t); Vec2d pos = curr.position * (1.0 - j * t) + next.position * (j * t);
auto [p_dist, p_near_l, auto [p_dist, p_near_l,
p_x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(pos.cast<AABBScalar>()); p_x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(pos.cast<AABBScalar>());
new_points.push_back(ExtendedPoint{pos, float(p_dist + boundary_offset), p_near_l}); ExtendedPoint new_p{};
new_p.position = pos;
new_p.distance = float(p_dist + boundary_offset);
new_points.push_back(new_p);
} }
} }
new_points.push_back(points.back()); new_points.push_back(points.back());
@ -207,6 +143,9 @@ std::vector<ExtendedPoint> estimate_points_properties(const std::vector<P>
points = new_points; points = new_points;
} }
std::vector<float> angles_for_curvature(points.size());
std::vector<float> distances_for_curvature(points.size());
for (int point_idx = 0; point_idx < int(points.size()); ++point_idx) { for (int point_idx = 0; point_idx < int(points.size()); ++point_idx) {
ExtendedPoint &a = points[point_idx]; ExtendedPoint &a = points[point_idx];
ExtendedPoint &prev = points[point_idx > 0 ? point_idx - 1 : point_idx]; ExtendedPoint &prev = points[point_idx > 0 ? point_idx - 1 : point_idx];
@ -214,22 +153,59 @@ std::vector<ExtendedPoint> estimate_points_properties(const std::vector<P>
int prev_point_idx = point_idx; int prev_point_idx = point_idx;
while (prev_point_idx > 0) { while (prev_point_idx > 0) {
prev_point_idx--; prev_point_idx--;
if ((a.position - points[prev_point_idx].position).squaredNorm() > EPSILON) { break; } if ((a.position - points[prev_point_idx].position).squaredNorm() > EPSILON) {
break;
}
} }
int next_point_index = point_idx; int next_point_index = point_idx;
while (next_point_index < int(points.size()) - 1) { while (next_point_index < int(points.size()) - 1) {
next_point_index++; next_point_index++;
if ((a.position - points[next_point_index].position).squaredNorm() > EPSILON) { break; } if ((a.position - points[next_point_index].position).squaredNorm() > EPSILON) {
break;
}
} }
distances_for_curvature[point_idx] = (prev.position - a.position).norm();
if (prev_point_idx != point_idx && next_point_index != point_idx) { if (prev_point_idx != point_idx && next_point_index != point_idx) {
float distance = (prev.position - a.position).norm(); float alfa = angle(a.position - points[prev_point_idx].position, points[next_point_index].position - a.position);
float alfa = angle(a.position - points[prev_point_idx].position, points[next_point_index].position - a.position); angles_for_curvature[point_idx] = alfa;
cestim.add_point(distance, alfa); } // else keep zero
} }
a.curvature = cestim.get_curvature(); for (float window_size : {3.0f, 9.0f, 16.0f}) {
size_t tail_point = 0;
float tail_window_acc = 0;
float tail_angle_acc = 0;
size_t head_point = 0;
float head_window_acc = 0;
float head_angle_acc = 0;
for (int point_idx = 0; point_idx < int(points.size()); ++point_idx) {
if (point_idx > 0) {
tail_window_acc += distances_for_curvature[point_idx - 1];
tail_angle_acc += angles_for_curvature[point_idx - 1];
head_window_acc -= distances_for_curvature[point_idx - 1];
head_angle_acc -= angles_for_curvature[point_idx - 1];
}
while (tail_window_acc > window_size * 0.5 && tail_point < point_idx) {
tail_window_acc -= distances_for_curvature[tail_point];
tail_angle_acc -= angles_for_curvature[tail_point];
tail_point++;
}
while (head_window_acc < window_size * 0.5 && head_point < int(points.size()) - 1) {
head_window_acc += distances_for_curvature[head_point];
head_angle_acc += angles_for_curvature[head_point];
head_point++;
}
float curvature = (tail_angle_acc + head_angle_acc) / (tail_window_acc + head_window_acc);
if (std::abs(curvature) > std::abs(points[point_idx].curvature)) {
points[point_idx].curvature = curvature;
}
}
} }
return points; return points;
@ -246,6 +222,8 @@ class ExtrusionQualityEstimator
{ {
std::unordered_map<const PrintObject *, AABBTreeLines::LinesDistancer<Linef>> prev_layer_boundaries; std::unordered_map<const PrintObject *, AABBTreeLines::LinesDistancer<Linef>> prev_layer_boundaries;
std::unordered_map<const PrintObject *, AABBTreeLines::LinesDistancer<Linef>> next_layer_boundaries; std::unordered_map<const PrintObject *, AABBTreeLines::LinesDistancer<Linef>> next_layer_boundaries;
std::unordered_map<const PrintObject *, AABBTreeLines::LinesDistancer<CurledLine>> prev_curled_extrusions;
std::unordered_map<const PrintObject *, AABBTreeLines::LinesDistancer<CurledLine>> next_curled_extrusions;
const PrintObject *current_object; const PrintObject *current_object;
public: public:
@ -253,18 +231,22 @@ public:
void prepare_for_new_layer(const Layer *layer) void prepare_for_new_layer(const Layer *layer)
{ {
if (layer == nullptr) return; if (layer == nullptr)
const PrintObject *object = layer->object(); return;
prev_layer_boundaries[object] = next_layer_boundaries[object]; const PrintObject *object = layer->object();
next_layer_boundaries[object] = AABBTreeLines::LinesDistancer<Linef>{to_unscaled_linesf(layer->lslices)}; prev_layer_boundaries[object] = next_layer_boundaries[object];
next_layer_boundaries[object] = AABBTreeLines::LinesDistancer<Linef>{to_unscaled_linesf(layer->lslices)};
prev_curled_extrusions[object] = next_curled_extrusions[object];
next_curled_extrusions[object] = AABBTreeLines::LinesDistancer<CurledLine>{layer->curled_lines};
} }
std::vector<ProcessedPoint> estimate_extrusion_quality(const ExtrusionPath &path, std::vector<ProcessedPoint> estimate_speed_from_extrusion_quality(
const std::vector<std::pair<int, ConfigOptionFloatOrPercent>> overhangs_w_speeds, const ExtrusionPath &path,
const std::vector<std::pair<int, ConfigOptionInts>> overhangs_w_fan_speeds, const std::vector<std::pair<int, ConfigOptionFloatOrPercent>> overhangs_w_speeds,
size_t extruder_id, const std::vector<std::pair<int, ConfigOptionInts>> overhangs_w_fan_speeds,
float ext_perimeter_speed, size_t extruder_id,
float original_speed) float ext_perimeter_speed,
float original_speed)
{ {
float speed_base = ext_perimeter_speed > 0 ? ext_perimeter_speed : original_speed; float speed_base = ext_perimeter_speed > 0 ? ext_perimeter_speed : original_speed;
std::map<float, float> speed_sections; std::map<float, float> speed_sections;
@ -272,6 +254,7 @@ public:
float distance = path.width * (1.0 - (overhangs_w_speeds[i].first / 100.0)); float distance = path.width * (1.0 - (overhangs_w_speeds[i].first / 100.0));
float speed = overhangs_w_speeds[i].second.percent ? (speed_base * overhangs_w_speeds[i].second.value / 100.0) : float speed = overhangs_w_speeds[i].second.percent ? (speed_base * overhangs_w_speeds[i].second.value / 100.0) :
overhangs_w_speeds[i].second.value; overhangs_w_speeds[i].second.value;
if (speed < EPSILON) speed = speed_base;
speed_sections[distance] = speed; speed_sections[distance] = speed;
} }
@ -291,6 +274,22 @@ public:
const ExtendedPoint &curr = extended_points[i]; const ExtendedPoint &curr = extended_points[i];
const ExtendedPoint &next = extended_points[i + 1 < extended_points.size() ? i + 1 : i]; const ExtendedPoint &next = extended_points[i + 1 < extended_points.size() ? i + 1 : i];
float artificial_distance_to_curled_lines = 0.0;
{
Vec2d middle = 0.5 * (curr.position + next.position);
auto line_indices = prev_curled_extrusions[current_object].all_lines_in_radius(Point::new_scale(middle),
scale_(10.0 * path.width));
for (size_t idx : line_indices) {
const CurledLine &line = prev_curled_extrusions[current_object].get_line(idx);
float distance_from_curled = unscaled(line_alg::distance_to(line, Point::new_scale(middle)));
float dist = path.width * (1.0 - (distance_from_curled / (10.0 * path.width))) *
(1.0 - (distance_from_curled / (10.0 * path.width))) *
(line.curled_height / (path.height * 10.0f)); // max_curled_height_factor from SupportSpotGenerator
artificial_distance_to_curled_lines = std::max(artificial_distance_to_curled_lines, dist);
}
}
auto interpolate_speed = [](const std::map<float, float> &values, float distance) { auto interpolate_speed = [](const std::map<float, float> &values, float distance) {
auto upper_dist = values.lower_bound(distance); auto upper_dist = values.lower_bound(distance);
if (upper_dist == values.end()) { if (upper_dist == values.end()) {
@ -305,12 +304,14 @@ public:
return (1.0f - t) * lower_dist->second + t * upper_dist->second; return (1.0f - t) * lower_dist->second + t * upper_dist->second;
}; };
float extrusion_speed = std::min(interpolate_speed(speed_sections, curr.distance), float extrusion_speed = std::min(interpolate_speed(speed_sections, curr.distance),
interpolate_speed(speed_sections, next.distance)); interpolate_speed(speed_sections, next.distance));
float fan_speed = std::min(interpolate_speed(fan_speed_sections, curr.distance), float curled_base_speed = interpolate_speed(speed_sections, artificial_distance_to_curled_lines);
interpolate_speed(fan_speed_sections, next.distance)); float final_speed = std::min(curled_base_speed, extrusion_speed);
float fan_speed = std::min(interpolate_speed(fan_speed_sections, curr.distance),
interpolate_speed(fan_speed_sections, next.distance));
processed_points.push_back({scaled(curr.position), extrusion_speed, int(fan_speed)}); processed_points.push_back({scaled(curr.position), final_speed, int(fan_speed)});
} }
return processed_points; return processed_points;
} }

View File

@ -3,6 +3,7 @@
#include "libslic3r/Print.hpp" #include "libslic3r/Print.hpp"
#include "libslic3r/LocalesUtils.hpp" #include "libslic3r/LocalesUtils.hpp"
#include "libslic3r/format.hpp" #include "libslic3r/format.hpp"
#include "libslic3r/I18N.hpp"
#include "libslic3r/GCodeWriter.hpp" #include "libslic3r/GCodeWriter.hpp"
#include "GCodeProcessor.hpp" #include "GCodeProcessor.hpp"
@ -441,9 +442,7 @@ void GCodeProcessorResult::reset() {
max_print_height = 0.0f; max_print_height = 0.0f;
settings_ids.reset(); settings_ids.reset();
extruders_count = 0; extruders_count = 0;
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
backtrace_enabled = false; backtrace_enabled = false;
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
extruder_colors = std::vector<std::string>(); extruder_colors = std::vector<std::string>();
filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER); filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY); filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY);
@ -461,9 +460,7 @@ void GCodeProcessorResult::reset() {
max_print_height = 0.0f; max_print_height = 0.0f;
settings_ids.reset(); settings_ids.reset();
extruders_count = 0; extruders_count = 0;
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
backtrace_enabled = false; backtrace_enabled = false;
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
extruder_colors = std::vector<std::string>(); extruder_colors = std::vector<std::string>();
filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER); filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY); filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY);
@ -557,9 +554,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
m_producer = EProducer::PrusaSlicer; m_producer = EProducer::PrusaSlicer;
m_flavor = config.gcode_flavor; m_flavor = config.gcode_flavor;
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
m_result.backtrace_enabled = is_XL_printer(config); m_result.backtrace_enabled = is_XL_printer(config);
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
size_t extruders_count = config.nozzle_diameter.values.size(); size_t extruders_count = config.nozzle_diameter.values.size();
m_result.extruders_count = extruders_count; m_result.extruders_count = extruders_count;
@ -3384,7 +3379,7 @@ void GCodeProcessor::process_T(const std::string_view command)
if (m_extruder_id != id) { if (m_extruder_id != id) {
if (((m_producer == EProducer::PrusaSlicer || m_producer == EProducer::Slic3rPE || m_producer == EProducer::Slic3r) && id >= m_result.extruders_count) || if (((m_producer == EProducer::PrusaSlicer || m_producer == EProducer::Slic3rPE || m_producer == EProducer::Slic3r) && id >= m_result.extruders_count) ||
((m_producer != EProducer::PrusaSlicer && m_producer != EProducer::Slic3rPE && m_producer != EProducer::Slic3r) && id >= m_result.extruder_colors.size())) ((m_producer != EProducer::PrusaSlicer && m_producer != EProducer::Slic3rPE && m_producer != EProducer::Slic3r) && id >= m_result.extruder_colors.size()))
BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid toolchange, maybe from a custom gcode."; BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid toolchange, maybe from a custom gcode (" << command << ").";
else { else {
unsigned char old_extruder_id = m_extruder_id; unsigned char old_extruder_id = m_extruder_id;
process_filaments(CustomGCode::ToolChange); process_filaments(CustomGCode::ToolChange);
@ -3476,7 +3471,6 @@ void GCodeProcessor::post_process()
last_exported_stop[i] = time_in_minutes(m_time_processor.machines[i].time); last_exported_stop[i] = time_in_minutes(m_time_processor.machines[i].time);
} }
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
// Helper class to modify and export gcode to file // Helper class to modify and export gcode to file
class ExportLines class ExportLines
{ {
@ -3566,7 +3560,7 @@ void GCodeProcessor::post_process()
++m_curr_g1_id; ++m_curr_g1_id;
} }
if (it != init_it || m_curr_g1_id == 0) if ((it != m_machine.g1_times_cache.end() && it != init_it) || m_curr_g1_id == 0)
m_time = it->elapsed_time; m_time = it->elapsed_time;
} }
@ -3610,7 +3604,7 @@ void GCodeProcessor::post_process()
last_time_insertion = rev_it->time; last_time_insertion = rev_it->time;
const std::string out_line = line_inserter(i + 1, last_time_insertion, m_time - last_time_insertion); const std::string out_line = line_inserter(i + 1, last_time_insertion, m_time - last_time_insertion);
rev_it_dist = std::distance(m_lines.rbegin(), rev_it) + 1; rev_it_dist = std::distance(m_lines.rbegin(), rev_it) + 1;
const auto new_it = m_lines.insert(rev_it.base(), { out_line, rev_it->time }); m_lines.insert(rev_it.base(), { out_line, rev_it->time });
#ifndef NDEBUG #ifndef NDEBUG
m_statistics.add_line(out_line.length()); m_statistics.add_line(out_line.length());
#endif // NDEBUG #endif // NDEBUG
@ -3711,26 +3705,15 @@ void GCodeProcessor::post_process()
}; };
ExportLines export_lines(m_result.backtrace_enabled ? ExportLines::EWriteType::ByTime : ExportLines::EWriteType::BySize, m_time_processor.machines[0]); ExportLines export_lines(m_result.backtrace_enabled ? ExportLines::EWriteType::ByTime : ExportLines::EWriteType::BySize, m_time_processor.machines[0]);
#else
// buffer line to export only when greater than 64K to reduce writing calls
std::string export_line;
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
// replace placeholder lines with the proper final value // replace placeholder lines with the proper final value
// gcode_line is in/out parameter, to reduce expensive memory allocation // gcode_line is in/out parameter, to reduce expensive memory allocation
auto process_placeholders = [&](std::string& gcode_line) { auto process_placeholders = [&](std::string& gcode_line) {
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
bool processed = false; bool processed = false;
#else
unsigned int extra_lines_count = 0;
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
// remove trailing '\n' // remove trailing '\n'
auto line = std::string_view(gcode_line).substr(0, gcode_line.length() - 1); auto line = std::string_view(gcode_line).substr(0, gcode_line.length() - 1);
#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
std::string ret;
#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
if (line.length() > 1) { if (line.length() > 1) {
line = line.substr(1); line = line.substr(1);
if (m_time_processor.export_remaining_time_enabled && if (m_time_processor.export_remaining_time_enabled &&
@ -3739,29 +3722,16 @@ void GCodeProcessor::post_process()
const TimeMachine& machine = m_time_processor.machines[i]; const TimeMachine& machine = m_time_processor.machines[i];
if (machine.enabled) { if (machine.enabled) {
// export pair <percent, remaining time> // export pair <percent, remaining time>
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
export_lines.append_line(format_line_M73_main(machine.line_m73_main_mask.c_str(), export_lines.append_line(format_line_M73_main(machine.line_m73_main_mask.c_str(),
(line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? 0 : 100, (line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? 0 : 100,
(line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? time_in_minutes(machine.time) : 0)); (line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? time_in_minutes(machine.time) : 0));
processed = true; processed = true;
#else
ret += format_line_M73_main(machine.line_m73_main_mask.c_str(),
(line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? 0 : 100,
(line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? time_in_minutes(machine.time) : 0);
++extra_lines_count;
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
// export remaining time to next printer stop // export remaining time to next printer stop
if (line == reserved_tag(ETags::First_Line_M73_Placeholder) && !machine.stop_times.empty()) { if (line == reserved_tag(ETags::First_Line_M73_Placeholder) && !machine.stop_times.empty()) {
const int to_export_stop = time_in_minutes(machine.stop_times.front().elapsed_time); const int to_export_stop = time_in_minutes(machine.stop_times.front().elapsed_time);
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
export_lines.append_line(format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop)); export_lines.append_line(format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop));
last_exported_stop[i] = to_export_stop; last_exported_stop[i] = to_export_stop;
#else
ret += format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop);
last_exported_stop[i] = to_export_stop;
++extra_lines_count;
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
} }
} }
} }
@ -3775,12 +3745,8 @@ void GCodeProcessor::post_process()
sprintf(buf, "; estimated printing time (%s mode) = %s\n", sprintf(buf, "; estimated printing time (%s mode) = %s\n",
(mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent", (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent",
get_time_dhms(machine.time).c_str()); get_time_dhms(machine.time).c_str());
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
export_lines.append_line(buf); export_lines.append_line(buf);
processed = true; processed = true;
#else
ret += buf;
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
} }
} }
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) { for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
@ -3791,25 +3757,14 @@ void GCodeProcessor::post_process()
sprintf(buf, "; estimated first layer printing time (%s mode) = %s\n", sprintf(buf, "; estimated first layer printing time (%s mode) = %s\n",
(mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent", (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent",
get_time_dhms(machine.layers_time.empty() ? 0.f : machine.layers_time.front()).c_str()); get_time_dhms(machine.layers_time.empty() ? 0.f : machine.layers_time.front()).c_str());
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
export_lines.append_line(buf); export_lines.append_line(buf);
processed = true; processed = true;
#else
ret += buf;
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
} }
} }
} }
} }
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
return processed; return processed;
#else
if (!ret.empty())
// Not moving the move operator on purpose, so that the gcode_line allocation will grow and it will not be reallocated after handful of lines are processed.
gcode_line = ret;
return std::tuple(!ret.empty(), (extra_lines_count == 0) ? extra_lines_count : extra_lines_count - 1);
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
}; };
std::vector<double> filament_mm(m_result.extruders_count, 0.0); std::vector<double> filament_mm(m_result.extruders_count, 0.0);
@ -3883,16 +3838,8 @@ void GCodeProcessor::post_process()
time_in_minutes, format_time_float, format_line_M73_main, format_line_M73_stop_int, format_line_M73_stop_float, time_in_last_minute, time_in_minutes, format_time_float, format_line_M73_main, format_line_M73_stop_int, format_line_M73_stop_float, time_in_last_minute,
// Caches, to be modified // Caches, to be modified
&g1_times_cache_it, &last_exported_main, &last_exported_stop, &g1_times_cache_it, &last_exported_main, &last_exported_stop,
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
&export_lines] &export_lines]
#else
// String output
&export_line]
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
(const size_t g1_lines_counter) { (const size_t g1_lines_counter) {
#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
unsigned int exported_lines_count = 0;
#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
if (m_time_processor.export_remaining_time_enabled) { if (m_time_processor.export_remaining_time_enabled) {
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) { for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
const TimeMachine& machine = m_time_processor.machines[i]; const TimeMachine& machine = m_time_processor.machines[i];
@ -3906,17 +3853,9 @@ void GCodeProcessor::post_process()
std::pair<int, int> to_export_main = { int(100.0f * it->elapsed_time / machine.time), std::pair<int, int> to_export_main = { int(100.0f * it->elapsed_time / machine.time),
time_in_minutes(machine.time - it->elapsed_time) }; time_in_minutes(machine.time - it->elapsed_time) };
if (last_exported_main[i] != to_export_main) { if (last_exported_main[i] != to_export_main) {
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
export_lines.append_line(format_line_M73_main(machine.line_m73_main_mask.c_str(), export_lines.append_line(format_line_M73_main(machine.line_m73_main_mask.c_str(),
to_export_main.first, to_export_main.second)); to_export_main.first, to_export_main.second));
#else
export_line += format_line_M73_main(machine.line_m73_main_mask.c_str(),
to_export_main.first, to_export_main.second);
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
last_exported_main[i] = to_export_main; last_exported_main[i] = to_export_main;
#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
++exported_lines_count;
#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
} }
// export remaining time to next printer stop // export remaining time to next printer stop
auto it_stop = std::upper_bound(machine.stop_times.begin(), machine.stop_times.end(), it->elapsed_time, auto it_stop = std::upper_bound(machine.stop_times.begin(), machine.stop_times.end(), it->elapsed_time,
@ -3926,15 +3865,8 @@ void GCodeProcessor::post_process()
if (last_exported_stop[i] != to_export_stop) { if (last_exported_stop[i] != to_export_stop) {
if (to_export_stop > 0) { if (to_export_stop > 0) {
if (last_exported_stop[i] != to_export_stop) { if (last_exported_stop[i] != to_export_stop) {
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
export_lines.append_line(format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop)); export_lines.append_line(format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop));
#else
export_line += format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop);
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
last_exported_stop[i] = to_export_stop; last_exported_stop[i] = to_export_stop;
#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
++exported_lines_count;
#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
} }
} }
else { else {
@ -3953,22 +3885,12 @@ void GCodeProcessor::post_process()
} }
if (is_last) { if (is_last) {
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
if (std::distance(machine.stop_times.begin(), it_stop) == static_cast<ptrdiff_t>(machine.stop_times.size() - 1)) if (std::distance(machine.stop_times.begin(), it_stop) == static_cast<ptrdiff_t>(machine.stop_times.size() - 1))
export_lines.append_line(format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop)); export_lines.append_line(format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop));
else else
export_lines.append_line(format_line_M73_stop_float(machine.line_m73_stop_mask.c_str(), time_in_last_minute(it_stop->elapsed_time - it->elapsed_time))); export_lines.append_line(format_line_M73_stop_float(machine.line_m73_stop_mask.c_str(), time_in_last_minute(it_stop->elapsed_time - it->elapsed_time)));
#else
if (std::distance(machine.stop_times.begin(), it_stop) == static_cast<ptrdiff_t>(machine.stop_times.size() - 1))
export_line += format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop);
else
export_line += format_line_M73_stop_float(machine.line_m73_stop_mask.c_str(), time_in_last_minute(it_stop->elapsed_time - it->elapsed_time));
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
last_exported_stop[i] = to_export_stop; last_exported_stop[i] = to_export_stop;
#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
++exported_lines_count;
#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
} }
} }
} }
@ -3977,12 +3899,8 @@ void GCodeProcessor::post_process()
} }
} }
} }
#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
return exported_lines_count;
#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
}; };
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
// add lines XXX to exported gcode // add lines XXX to exported gcode
auto process_line_T = [this, &export_lines](const std::string& gcode_line, const size_t g1_lines_counter, const ExportLines::Backtrace& backtrace) { auto process_line_T = [this, &export_lines](const std::string& gcode_line, const size_t g1_lines_counter, const ExportLines::Backtrace& backtrace) {
const std::string cmd = GCodeReader::GCodeLine::extract_cmd(gcode_line); const std::string cmd = GCodeReader::GCodeLine::extract_cmd(gcode_line);
@ -3991,6 +3909,18 @@ void GCodeProcessor::post_process()
int tool_number = -1; int tool_number = -1;
ss >> tool_number; ss >> tool_number;
if (tool_number != -1) if (tool_number != -1)
if (tool_number < 0 || (int)m_extruder_temps_config.size() <= tool_number) {
// found an invalid value, clamp it to a valid one
tool_number = std::clamp<int>(0, m_extruder_temps_config.size() - 1, tool_number);
// emit warning
std::string warning = _u8L("GCode Post-Processor encountered an invalid toolchange, maybe from a custom gcode:");
warning += "\n> ";
warning += gcode_line;
warning += _u8L("Generated M104 lines may be incorrect.");
BOOST_LOG_TRIVIAL(error) << warning;
if (m_print != nullptr)
m_print->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, warning);
}
export_lines.insert_lines(backtrace, cmd, export_lines.insert_lines(backtrace, cmd,
// line inserter // line inserter
[tool_number, this](unsigned int id, float time, float time_diff) { [tool_number, this](unsigned int id, float time, float time_diff) {
@ -4015,37 +3945,15 @@ void GCodeProcessor::post_process()
}); });
} }
}; };
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
m_result.lines_ends.clear(); m_result.lines_ends.clear();
#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
// helper function to write to disk
size_t out_file_pos = 0;
auto write_string = [this, &export_line, &out, &out_path, &out_file_pos](const std::string& str) {
fwrite((const void*)export_line.c_str(), 1, export_line.length(), out.f);
if (ferror(out.f)) {
out.close();
boost::nowide::remove(out_path.c_str());
throw Slic3r::RuntimeError(std::string("GCode processor post process export failed.\nIs the disk full?\n"));
}
for (size_t i = 0; i < export_line.size(); ++i)
if (export_line[i] == '\n')
m_result.lines_ends.emplace_back(out_file_pos + i + 1);
out_file_pos += export_line.size();
export_line.clear();
};
#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
unsigned int line_id = 0; unsigned int line_id = 0;
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
// Backtrace data for Tx gcode lines // Backtrace data for Tx gcode lines
static const ExportLines::Backtrace backtrace_T = { 120.0f, 10 }; static const ExportLines::Backtrace backtrace_T = { 120.0f, 10 };
// In case there are multiple sources of backtracing, keeps track of the longest backtrack time needed // In case there are multiple sources of backtracing, keeps track of the longest backtrack time needed
// to flush the backtrace cache accordingly // to flush the backtrace cache accordingly
float max_backtrace_time = 120.0f; float max_backtrace_time = 120.0f;
#else
std::vector<std::pair<unsigned int, unsigned int>> offsets;
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
{ {
// Read the input stream 64kB at a time, extract lines and process them. // Read the input stream 64kB at a time, extract lines and process them.
@ -4069,24 +3977,15 @@ void GCodeProcessor::post_process()
gcode_line.insert(gcode_line.end(), it, it_end); gcode_line.insert(gcode_line.end(), it, it_end);
if (eol) { if (eol) {
++line_id; ++line_id;
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
export_lines.update(line_id, g1_lines_counter); export_lines.update(line_id, g1_lines_counter);
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
gcode_line += "\n"; gcode_line += "\n";
// replace placeholder lines // replace placeholder lines
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
bool processed = process_placeholders(gcode_line); bool processed = process_placeholders(gcode_line);
if (processed) if (processed)
gcode_line.clear(); gcode_line.clear();
#else
auto [processed, lines_added_count] = process_placeholders(gcode_line);
if (processed && lines_added_count > 0)
offsets.push_back({ line_id, lines_added_count });
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
if (!processed) if (!processed)
processed = process_used_filament(gcode_line); processed = process_used_filament(gcode_line);
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
if (!processed && !is_temporary_decoration(gcode_line)) { if (!processed && !is_temporary_decoration(gcode_line)) {
if (GCodeReader::GCodeLine::cmd_is(gcode_line, "G1")) if (GCodeReader::GCodeLine::cmd_is(gcode_line, "G1"))
// add lines M73 where needed // add lines M73 where needed
@ -4101,18 +4000,6 @@ void GCodeProcessor::post_process()
if (!gcode_line.empty()) if (!gcode_line.empty())
export_lines.append_line(gcode_line); export_lines.append_line(gcode_line);
export_lines.write(out, 1.1f * max_backtrace_time, m_result, out_path); export_lines.write(out, 1.1f * max_backtrace_time, m_result, out_path);
#else
if (!processed && !is_temporary_decoration(gcode_line) && GCodeReader::GCodeLine::cmd_is(gcode_line, "G1")) {
// remove temporary lines, add lines M73 where needed
unsigned int extra_lines_count = process_line_G1(g1_lines_counter++);
if (extra_lines_count > 0)
offsets.push_back({ line_id, extra_lines_count });
}
export_line += gcode_line;
if (export_line.length() > 65535)
write_string(export_line);
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
gcode_line.clear(); gcode_line.clear();
} }
// Skip EOL. // Skip EOL.
@ -4127,30 +4014,12 @@ void GCodeProcessor::post_process()
} }
} }
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
export_lines.flush(out, m_result, out_path); export_lines.flush(out, m_result, out_path);
#else
if (!export_line.empty())
write_string(export_line);
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
out.close(); out.close();
in.close(); in.close();
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
export_lines.synchronize_moves(m_result); export_lines.synchronize_moves(m_result);
#else
// updates moves' gcode ids which have been modified by the insertion of the M73 lines
unsigned int curr_offset_id = 0;
unsigned int total_offset = 0;
for (GCodeProcessorResult::MoveVertex& move : m_result.moves) {
while (curr_offset_id < static_cast<unsigned int>(offsets.size()) && offsets[curr_offset_id].first <= move.gcode_id) {
total_offset += offsets[curr_offset_id].second;
++curr_offset_id;
}
move.gcode_id += total_offset;
}
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
if (rename_file(out_path, m_result.filename)) if (rename_file(out_path, m_result.filename))
throw Slic3r::RuntimeError(std::string("Failed to rename the output G-code file from ") + out_path + " to " + m_result.filename + '\n' + throw Slic3r::RuntimeError(std::string("Failed to rename the output G-code file from ") + out_path + " to " + m_result.filename + '\n' +

View File

@ -16,6 +16,8 @@
namespace Slic3r { namespace Slic3r {
class Print;
enum class EMoveType : unsigned char enum class EMoveType : unsigned char
{ {
Noop, Noop,
@ -125,9 +127,7 @@ namespace Slic3r {
float max_print_height; float max_print_height;
SettingsIds settings_ids; SettingsIds settings_ids;
size_t extruders_count; size_t extruders_count;
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
bool backtrace_enabled; bool backtrace_enabled;
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
std::vector<std::string> extruder_colors; std::vector<std::string> extruder_colors;
std::vector<float> filament_diameters; std::vector<float> filament_diameters;
std::vector<float> filament_densities; std::vector<float> filament_densities;
@ -588,6 +588,8 @@ namespace Slic3r {
TimeProcessor m_time_processor; TimeProcessor m_time_processor;
UsedFilaments m_used_filaments; UsedFilaments m_used_filaments;
Print* m_print{ nullptr };
GCodeProcessorResult m_result; GCodeProcessorResult m_result;
static unsigned int s_result_id; static unsigned int s_result_id;
@ -601,6 +603,8 @@ namespace Slic3r {
GCodeProcessor(); GCodeProcessor();
void apply_config(const PrintConfig& config); void apply_config(const PrintConfig& config);
void set_print(Print* print) { m_print = print; }
void enable_stealth_time_estimator(bool enabled); void enable_stealth_time_estimator(bool enabled);
bool is_stealth_time_estimator_enabled() const { bool is_stealth_time_estimator_enabled() const {
return m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].enabled; return m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].enabled;

View File

@ -437,7 +437,7 @@ Polygons extract_perimeter_polygons(const Layer *layer, std::vector<const LayerR
if (polygons.empty()) { // If there are no perimeter polygons for whatever reason (disabled perimeters .. ) insert dummy point if (polygons.empty()) { // If there are no perimeter polygons for whatever reason (disabled perimeters .. ) insert dummy point
// it is easier than checking everywhere if the layer is not emtpy, no seam will be placed to this layer anyway // it is easier than checking everywhere if the layer is not emtpy, no seam will be placed to this layer anyway
polygons.emplace_back(std::vector { Point { 0, 0 } }); polygons.emplace_back(Points{ { 0, 0 } });
corresponding_regions_out.push_back(nullptr); corresponding_regions_out.push_back(nullptr);
} }

View File

@ -71,7 +71,6 @@ public:
return strncmp(cmd, cmd_test, len) == 0 && GCodeReader::is_end_of_word(cmd[len]); return strncmp(cmd, cmd_test, len) == 0 && GCodeReader::is_end_of_word(cmd[len]);
} }
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
static bool cmd_starts_with(const std::string& gcode_line, const char* cmd_test) { static bool cmd_starts_with(const std::string& gcode_line, const char* cmd_test) {
return strncmp(GCodeReader::skip_whitespaces(gcode_line.c_str()), cmd_test, strlen(cmd_test)) == 0; return strncmp(GCodeReader::skip_whitespaces(gcode_line.c_str()), cmd_test, strlen(cmd_test)) == 0;
} }
@ -82,7 +81,6 @@ public:
const std::string_view cmd = temp.cmd(); const std::string_view cmd = temp.cmd();
return { cmd.begin(), cmd.end() }; return { cmd.begin(), cmd.end() };
} }
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
private: private:
std::string m_raw; std::string m_raw;

View File

@ -12,11 +12,6 @@
namespace Slic3r { namespace Slic3r {
namespace ClipperLib {
class PolyNode;
using PolyNodes = std::vector<PolyNode*>;
}
namespace Geometry { namespace Geometry {
// Generic result of an orientation predicate. // Generic result of an orientation predicate.

View File

@ -106,6 +106,7 @@ Slic3r::Pointfs Slic3r::intersection_points(const ExPolygons &expolygons)
#include <libslic3r/BoundingBox.hpp> #include <libslic3r/BoundingBox.hpp>
namespace priv { namespace priv {
//FIXME O(n^2) complexity!
Slic3r::Pointfs compute_intersections(const Slic3r::Lines &lines) Slic3r::Pointfs compute_intersections(const Slic3r::Lines &lines)
{ {
using namespace Slic3r; using namespace Slic3r;

View File

@ -6,6 +6,7 @@
namespace Slic3r { namespace Slic3r {
// collect all intersecting points // collect all intersecting points
//FIXME O(n^2) complexity!
Pointfs intersection_points(const Lines &lines); Pointfs intersection_points(const Lines &lines);
Pointfs intersection_points(const Polygon &polygon); Pointfs intersection_points(const Polygon &polygon);
Pointfs intersection_points(const Polygons &polygons); Pointfs intersection_points(const Polygons &polygons);

View File

@ -19,6 +19,8 @@
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include <oneapi/tbb/scalable_allocator.h>
//#define DEBUG_FILES //#define DEBUG_FILES
#ifdef DEBUG_FILES #ifdef DEBUG_FILES
#include "libslic3r/SVG.hpp" #include "libslic3r/SVG.hpp"
@ -211,8 +213,8 @@ void JPSPathFinder::add_obstacles(const Layer *layer, const Point &global_origin
this->print_z = layer->print_z; this->print_z = layer->print_z;
Lines obstacles; Lines obstacles;
obstacles.reserve(layer->malformed_lines.size()); obstacles.reserve(layer->curled_lines.size());
for (const Line &l : layer->malformed_lines) { obstacles.push_back(Line{l.a + global_origin, l.b + global_origin}); } for (const Line &l : layer->curled_lines) { obstacles.push_back(Line{l.a + global_origin, l.b + global_origin}); }
add_obstacles(obstacles); add_obstacles(obstacles);
} }
@ -267,7 +269,7 @@ Polyline JPSPathFinder::find_path(const Point &p0, const Point &p1)
using QNode = astar::QNode<JPSTracer<Pixel, decltype(cell_query)>>; using QNode = astar::QNode<JPSTracer<Pixel, decltype(cell_query)>>;
std::unordered_map<size_t, QNode> astar_cache{}; std::unordered_map<size_t, QNode> astar_cache{};
std::vector<Pixel> out_path; std::vector<Pixel, PointsAllocator<Pixel>> out_path;
std::vector<decltype(tracer)::Node> out_nodes; std::vector<decltype(tracer)::Node> out_nodes;
if (!astar::search_route(tracer, {start, {0, 0}}, std::back_inserter(out_nodes), astar_cache)) { if (!astar::search_route(tracer, {start, {0, 0}}, std::back_inserter(out_nodes), astar_cache)) {
@ -306,7 +308,7 @@ Polyline JPSPathFinder::find_path(const Point &p0, const Point &p1)
svg.draw(scaled_point(start), "green", scale_(0.4)); svg.draw(scaled_point(start), "green", scale_(0.4));
#endif #endif
std::vector<Pixel> tmp_path; std::vector<Pixel, PointsAllocator<Pixel>> tmp_path;
tmp_path.reserve(out_path.size()); tmp_path.reserve(out_path.size());
// Some path found, reverse and remove points that do not change direction // Some path found, reverse and remove points that do not change direction
std::reverse(out_path.begin(), out_path.end()); std::reverse(out_path.begin(), out_path.end());

View File

@ -60,7 +60,10 @@ void Layer::make_slices()
} }
// used by Layer::build_up_down_graph() // used by Layer::build_up_down_graph()
[[nodiscard]] static ClipperLib_Z::Paths expolygons_to_zpaths(const ExPolygons &expolygons, coord_t isrc) // Shrink source polygons one by one, so that they will be separated if they were touching
// at vertices (non-manifold situation).
// Then convert them to Z-paths with Z coordinate indicating index of the source expolygon.
[[nodiscard]] static ClipperLib_Z::Paths expolygons_to_zpaths_shrunk(const ExPolygons &expolygons, coord_t isrc)
{ {
size_t num_paths = 0; size_t num_paths = 0;
for (const ExPolygon &expolygon : expolygons) for (const ExPolygon &expolygon : expolygons)
@ -69,15 +72,89 @@ void Layer::make_slices()
ClipperLib_Z::Paths out; ClipperLib_Z::Paths out;
out.reserve(num_paths); out.reserve(num_paths);
for (const ExPolygon &expolygon : expolygons) { ClipperLib::Paths contours;
for (size_t icontour = 0; icontour < expolygon.num_contours(); ++ icontour) { ClipperLib::Paths holes;
const Polygon &contour = expolygon.contour_or_hole(icontour); ClipperLib::Clipper clipper;
out.emplace_back(); ClipperLib::ClipperOffset co;
ClipperLib_Z::Path &path = out.back(); ClipperLib::Paths out2;
path.reserve(contour.size());
for (const Point &p : contour.points) // Top / bottom surfaces must overlap more than 2um to be chained into a Z graph.
path.push_back({ p.x(), p.y(), isrc }); // Also a larger offset will likely be more robust on non-manifold input polygons.
static constexpr const float delta = scaled<float>(0.001);
co.MiterLimit = scaled<double>(3.);
// Use the default zero edge merging distance. For this kind of safety offset the accuracy of normal direction is not important.
// co.ShortestEdgeLength = delta * ClipperOffsetShortestEdgeFactor;
static constexpr const double accept_area_threshold_ccw = sqr(scaled<double>(0.1 * delta));
// Such a small hole should not survive the shrinkage, it should grow over
static constexpr const double accept_area_threshold_cw = sqr(scaled<double>(0.2 * delta));
for (const ExPolygon &expoly : expolygons) {
contours.clear();
co.Clear();
co.AddPath(expoly.contour.points, ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
co.Execute(contours, - delta);
size_t num_prev = out.size();
if (! contours.empty()) {
holes.clear();
for (const Polygon &hole : expoly.holes) {
co.Clear();
co.AddPath(hole.points, ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
// Execute reorients the contours so that the outer most contour has a positive area. Thus the output
// contours will be CCW oriented even though the input paths are CW oriented.
// Offset is applied after contour reorientation, thus the signum of the offset value is reversed.
out2.clear();
co.Execute(out2, delta);
append(holes, std::move(out2));
}
// Subtract holes from the contours.
if (! holes.empty()) {
clipper.Clear();
clipper.AddPaths(contours, ClipperLib::ptSubject, true);
clipper.AddPaths(holes, ClipperLib::ptClip, true);
contours.clear();
clipper.Execute(ClipperLib::ctDifference, contours, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
}
for (const auto &contour : contours) {
bool accept = true;
// Trying to get rid of offset artifacts, that may be created due to numerical issues in offsetting algorithm
// or due to self-intersections in the source polygons.
//FIXME how reliable is it? Is it helpful or harmful? It seems to do more harm than good as it tends to punch holes
// into existing ExPolygons.
#if 0
if (contour.size() < 8) {
// Only accept contours with area bigger than some threshold.
double a = ClipperLib::Area(contour);
// Polygon has to be bigger than some threshold to be accepted.
// Hole to be accepted has to have an area slightly bigger than the non-hole, so it will not happen due to rounding errors,
// that a hole will be accepted without its outer contour.
accept = a > 0 ? a > accept_area_threshold_ccw : a < - accept_area_threshold_cw;
}
#endif
if (accept) {
out.emplace_back();
ClipperLib_Z::Path &path = out.back();
path.reserve(contour.size());
for (const Point &p : contour)
path.push_back({ p.x(), p.y(), isrc });
}
}
} }
#if 0 // #ifndef NDEBUG
// Test whether the expolygons in a single layer overlap.
Polygons test;
for (size_t i = num_prev; i < out.size(); ++ i)
test.emplace_back(ClipperZUtils::from_zpath(out[i]));
Polygons outside = diff(test, to_polygons(expoly));
if (! outside.empty()) {
BoundingBox bbox(get_extents(expoly));
bbox.merge(get_extents(test));
SVG svg(debug_out_path("expolygons_to_zpaths_shrunk-self-intersections.svg").c_str(), bbox);
svg.draw(expoly, "blue");
svg.draw(test, "green");
svg.draw(outside, "red");
}
assert(outside.empty());
#endif // NDEBUG
++ isrc; ++ isrc;
} }
@ -126,120 +203,36 @@ static void connect_layer_slices(
if (polynode.Contour.size() >= 3) { if (polynode.Contour.size() >= 3) {
// If there is an intersection point, it should indicate which contours (one from layer below, the other from layer above) intersect. // If there is an intersection point, it should indicate which contours (one from layer below, the other from layer above) intersect.
// Otherwise the contour is fully inside another contour. // Otherwise the contour is fully inside another contour.
int32_t i = -1, j = -1; auto [i, j] = this->find_top_bottom_contour_ids_strict(polynode);
for (int icontour = 0; icontour <= polynode.ChildCount(); ++ icontour) { bool found = false;
const ClipperLib_Z::Path &contour = icontour == 0 ? polynode.Contour : polynode.Childs[icontour - 1]->Contour; if (i < 0 && j < 0) {
if (contour.size() >= 3) { // This should not happen. It may only happen if the source contours had just self intersections or intersections with contours at the same layer.
for (const ClipperLib_Z::IntPoint &pt : contour) { // We may safely ignore such cases where the intersection area is meager.
j = pt.z(); double a = ClipperLib_Z::Area(polynode.Contour);
if (j < 0) { if (a < sqr(scaled<double>(0.001))) {
const auto &intersection = m_intersections[-j - 1]; // Ignore tiny overlaps. They are not worth resolving.
assert(intersection.first <= intersection.second); } else {
if (intersection.second < m_offset_above) { // We should not ignore large cases. Try to resolve the conflict by a majority of references.
// Ignore intersection of polygons on the 1st layer. std::tie(i, j) = this->find_top_bottom_contour_ids_approx(polynode);
assert(intersection.first >= m_offset_below); // At least top or bottom should be resolved.
j = i; assert(i >= 0 || j >= 0);
} else if (intersection.first >= m_offset_above) {
// Ignore intersection of polygons on the 2nd layer
assert(intersection.second < m_offset_end);
j = i;
} else {
std::tie(i, j) = m_intersections[-j - 1];
assert(assert_intersection_valid(i, j));
goto end;
}
} else if (i == -1) {
// First source contour of this expolygon was found.
i = j;
} else if (i != j) {
// Second source contour of this expolygon was found.
if (i > j)
std::swap(i, j);
assert(assert_intersection_valid(i, j));
goto end;
}
}
} }
} }
end: if (j < 0) {
bool found = false; if (i < 0) {
if (i == -1) { // this->find_top_bottom_contour_ids_approx() shoudl have made sure this does not happen.
// This should not happen. It may only happen if the source contours had just self intersections or intersections with contours at the same layer. assert(false);
assert(false);
} else if (i == j) {
// The contour is completely inside another contour.
Point pt(polynode.Contour.front().x(), polynode.Contour.front().y());
if (i < m_offset_above) {
// Index of an island below. Look-it up in the island above.
assert(i >= m_offset_below);
i -= m_offset_below;
for (int l = int(m_above.lslices_ex.size()) - 1; l >= 0; -- l) {
LayerSlice &lslice = m_above.lslices_ex[l];
if (lslice.bbox.contains(pt) && m_above.lslices[l].contains(pt)) {
found = true;
j = l;
assert(i >= 0 && i < m_below.lslices_ex.size());
assert(j >= 0 && j < m_above.lslices_ex.size());
break;
}
}
if (!found) {
// The check above might sometimes fail when the polygons overlap only on points, which causes the clipper to detect no intersection.
// The problem happens rarely, mostly on simple polygons (in terms of number of points), but regardless of size!
// example of failing link on two layers, each with single polygon without holes.
// layer A = Polygon{(-24931238,-11153865),(-22504249,-8726874),(-22504249,11477151),(-23261469,12235585),(-23752371,12727276),(-25002495,12727276),(-27502745,10227026),(-27502745,-12727274),(-26504645,-12727274)}
// layer B = Polygon{(-24877897,-11100524),(-22504249,-8726874),(-22504249,11477151),(-23244827,12218916),(-23752371,12727276),(-25002495,12727276),(-27502745,10227026),(-27502745,-12727274),(-26504645,-12727274)}
// note that first point is not identical, and the check above picks (-24877897,-11100524) as the first contour point (polynode.Contour.front()).
// that point is sadly slightly outisde of the layer A, so no link is detected, eventhough they are overlaping "completely"
Polygon contour_poly;
for (const auto& p : polynode.Contour) {
contour_poly.points.emplace_back(p.x(), p.y());
}
BoundingBox contour_aabb{contour_poly.points};
for (int l = int(m_above.lslices_ex.size()) - 1; l >= 0; --l) {
LayerSlice &lslice = m_above.lslices_ex[l];
// it is potentially slow, but should be executed rarely
if (contour_aabb.overlap(lslice.bbox) && !intersection(Polygons{contour_poly}, m_above.lslices[l]).empty()) {
found = true;
j = l;
assert(i >= 0 && i < m_below.lslices_ex.size());
assert(j >= 0 && j < m_above.lslices_ex.size());
break;
}
}
}
} else { } else {
// Index of an island above. Look-it up in the island below. assert(i >= m_offset_below && i < m_offset_above);
assert(j < m_offset_end); i -= m_offset_below;
j -= m_offset_above; j = this->find_other_contour_costly(polynode, m_above, j == -2);
for (int l = int(m_below.lslices_ex.size()) - 1; l >= 0; -- l) { found = j >= 0;
LayerSlice &lslice = m_below.lslices_ex[l];
if (lslice.bbox.contains(pt) && m_below.lslices[l].contains(pt)) {
found = true;
i = l;
assert(i >= 0 && i < m_below.lslices_ex.size());
assert(j >= 0 && j < m_above.lslices_ex.size());
break;
}
}
if (!found) { // Explanation for aditional check is above.
Polygon contour_poly;
for (const auto &p : polynode.Contour) {
contour_poly.points.emplace_back(p.x(), p.y());
}
BoundingBox contour_aabb{contour_poly.points};
for (int l = int(m_below.lslices_ex.size()) - 1; l >= 0; --l) {
LayerSlice &lslice = m_below.lslices_ex[l];
if (contour_aabb.overlap(lslice.bbox) && !intersection(Polygons{contour_poly}, m_below.lslices[l]).empty()) {
found = true;
i = l;
assert(i >= 0 && i < m_below.lslices_ex.size());
assert(j >= 0 && j < m_above.lslices_ex.size());
break;
}
}
}
} }
} else if (i < 0) {
assert(j >= m_offset_above && j < m_offset_end);
j -= m_offset_above;
i = this->find_other_contour_costly(polynode, m_below, i == -2);
found = i >= 0;
} else { } else {
assert(assert_intersection_valid(i, j)); assert(assert_intersection_valid(i, j));
i -= m_offset_below; i -= m_offset_below;
@ -249,6 +242,8 @@ static void connect_layer_slices(
found = true; found = true;
} }
if (found) { if (found) {
assert(i >= 0 && i < m_below.lslices_ex.size());
assert(j >= 0 && j < m_above.lslices_ex.size());
// Subtract area of holes from the area of outer contour. // Subtract area of holes from the area of outer contour.
double area = ClipperLib_Z::Area(polynode.Contour); double area = ClipperLib_Z::Area(polynode.Contour);
for (int icontour = 0; icontour < polynode.ChildCount(); ++ icontour) for (int icontour = 0; icontour < polynode.ChildCount(); ++ icontour)
@ -288,6 +283,187 @@ static void connect_layer_slices(
} }
private: private:
// Find the indices of the contour below & above for an expolygon created as an intersection of two expolygons, one below, the other above.
// Returns -1 if there is no point on the intersection refering bottom resp. top source expolygon.
// Returns -2 if the intersection refers to multiple source expolygons on bottom resp. top layers.
std::pair<int32_t, int32_t> find_top_bottom_contour_ids_strict(const ClipperLib_Z::PolyNode &polynode) const
{
// If there is an intersection point, it should indicate which contours (one from layer below, the other from layer above) intersect.
// Otherwise the contour is fully inside another contour.
int32_t i = -1, j = -1;
auto process_i = [&i, &j](coord_t k) {
if (i == -1)
i = k;
else if (i >= 0) {
if (i != k) {
// Error: Intersection contour contains points of two or more source bottom contours.
i = -2;
if (j == -2)
// break
return true;
}
} else
assert(i == -2);
return false;
};
auto process_j = [&i, &j](coord_t k) {
if (j == -1)
j = k;
else if (j >= 0) {
if (j != k) {
// Error: Intersection contour contains points of two or more source top contours.
j = -2;
if (i == -2)
// break
return true;
}
} else
assert(j == -2);
return false;
};
for (int icontour = 0; icontour <= polynode.ChildCount(); ++ icontour) {
const ClipperLib_Z::Path &contour = icontour == 0 ? polynode.Contour : polynode.Childs[icontour - 1]->Contour;
if (contour.size() >= 3) {
for (const ClipperLib_Z::IntPoint &pt : contour)
if (coord_t k = pt.z(); k < 0) {
const auto &intersection = m_intersections[-k - 1];
assert(intersection.first <= intersection.second);
if (intersection.first < m_offset_above ? process_i(intersection.first) : process_j(intersection.first))
goto end;
if (intersection.second < m_offset_above ? process_i(intersection.second) : process_j(intersection.second))
goto end;
} else if (k < m_offset_above ? process_i(k) : process_j(k))
goto end;
}
}
end:
return { i, j };
}
// Find the indices of the contour below & above for an expolygon created as an intersection of two expolygons, one below, the other above.
// This variant expects that the source expolygon assingment is not unique, it counts the majority.
// Returns -1 if there is no point on the intersection refering bottom resp. top source expolygon.
// Returns -2 if the intersection refers to multiple source expolygons on bottom resp. top layers.
std::pair<int32_t, int32_t> find_top_bottom_contour_ids_approx(const ClipperLib_Z::PolyNode &polynode) const
{
// 1) Collect histogram of contour references.
struct HistoEl {
int32_t id;
int32_t count;
};
std::vector<HistoEl> histogram;
{
auto increment_counter = [&histogram](const int32_t i) {
auto it = std::lower_bound(histogram.begin(), histogram.end(), i, [](auto l, auto r){ return l.id < r; });
if (it == histogram.end() || it->id != i)
histogram.insert(it, HistoEl{ i, int32_t(1) });
else
++ it->count;
};
for (int icontour = 0; icontour <= polynode.ChildCount(); ++ icontour) {
const ClipperLib_Z::Path &contour = icontour == 0 ? polynode.Contour : polynode.Childs[icontour - 1]->Contour;
if (contour.size() >= 3) {
for (const ClipperLib_Z::IntPoint &pt : contour)
if (coord_t k = pt.z(); k < 0) {
const auto &intersection = m_intersections[-k - 1];
assert(intersection.first <= intersection.second);
increment_counter(intersection.first);
increment_counter(intersection.second);
} else
increment_counter(k);
}
}
assert(! histogram.empty());
}
int32_t i = -1;
int32_t j = -1;
if (! histogram.empty()) {
// 2) Split the histogram to bottom / top.
auto mid = std::upper_bound(histogram.begin(), histogram.end(), m_offset_above, [](auto l, auto r){ return l < r.id; });
// 3) Sort the bottom / top parts separately.
auto bottom_begin = histogram.begin();
auto bottom_end = mid;
auto top_begin = mid;
auto top_end = histogram.end();
std::sort(bottom_begin, bottom_end, [](auto l, auto r) { return l.count > r.count; });
std::sort(top_begin, top_end, [](auto l, auto r) { return l.count > r.count; });
double i_quality = 0;
double j_quality = 0;
if (bottom_begin != bottom_end) {
i = bottom_begin->id;
i_quality = std::next(bottom_begin) == bottom_end ? std::numeric_limits<double>::max() : double(bottom_begin->count) / std::next(bottom_begin)->count;
}
if (top_begin != top_end) {
j = top_begin->id;
j_quality = std::next(top_begin) == top_end ? std::numeric_limits<double>::max() : double(top_begin->count) / std::next(top_begin)->count;
}
// Expected to be called only if there are duplicate references to be resolved by the histogram.
assert(i >= 0 || j >= 0);
assert(i_quality < std::numeric_limits<double>::max() || j_quality < std::numeric_limits<double>::max());
if (i >= 0 && i_quality < j_quality) {
// Force the caller to resolve the bottom references the costly but robust way.
assert(j >= 0);
// Twice the number of references for the best contour.
assert(j_quality >= 2.);
i = -2;
} else if (j >= 0) {
// Force the caller to resolve the top reference the costly but robust way.
assert(i >= 0);
// Twice the number of references for the best contour.
assert(i_quality >= 2.);
j = -2;
}
}
return { i, j };
}
static int32_t find_other_contour_costly(const ClipperLib_Z::PolyNode &polynode, const Layer &other_layer, bool other_has_duplicates)
{
if (! other_has_duplicates) {
// The contour below is likely completely inside another contour above. Look-it up in the island above.
Point pt(polynode.Contour.front().x(), polynode.Contour.front().y());
for (int i = int(other_layer.lslices_ex.size()) - 1; i >= 0; -- i)
if (other_layer.lslices_ex[i].bbox.contains(pt) && other_layer.lslices[i].contains(pt))
return i;
// The following shall not happen now as the source expolygons are being shrunk a bit before intersecting,
// thus each point of each intersection polygon should fit completely inside one of the original (unshrunk) expolygons.
assert(false);
}
// The comment below may not be valid anymore, see the comment above. However the code is used in case the polynode contains multiple references
// to other_layer expolygons, thus the references are not unique.
//
// The check above might sometimes fail when the polygons overlap only on points, which causes the clipper to detect no intersection.
// The problem happens rarely, mostly on simple polygons (in terms of number of points), but regardless of size!
// example of failing link on two layers, each with single polygon without holes.
// layer A = Polygon{(-24931238,-11153865),(-22504249,-8726874),(-22504249,11477151),(-23261469,12235585),(-23752371,12727276),(-25002495,12727276),(-27502745,10227026),(-27502745,-12727274),(-26504645,-12727274)}
// layer B = Polygon{(-24877897,-11100524),(-22504249,-8726874),(-22504249,11477151),(-23244827,12218916),(-23752371,12727276),(-25002495,12727276),(-27502745,10227026),(-27502745,-12727274),(-26504645,-12727274)}
// note that first point is not identical, and the check above picks (-24877897,-11100524) as the first contour point (polynode.Contour.front()).
// that point is sadly slightly outisde of the layer A, so no link is detected, eventhough they are overlaping "completely"
Polygons contour_poly{ Polygon{ClipperZUtils::from_zpath(polynode.Contour)} };
BoundingBox contour_aabb{contour_poly.front().points};
int32_t i_largest = -1;
double a_largest = 0;
for (int i = int(other_layer.lslices_ex.size()) - 1; i >= 0; -- i)
if (contour_aabb.overlap(other_layer.lslices_ex[i].bbox))
// it is potentially slow, but should be executed rarely
if (Polygons overlap = intersection(contour_poly, other_layer.lslices[i]); ! overlap.empty())
if (other_has_duplicates) {
// Find the contour with the largest overlap. It is expected that the other overlap will be very small.
double a = area(overlap);
if (a > a_largest) {
a_largest = a;
i_largest = i;
}
} else {
// Most likely there is just one contour that overlaps, however it is not guaranteed.
i_largest = i;
break;
}
assert(i_largest >= 0);
return i_largest;
}
const std::vector<std::pair<coord_t, coord_t>> &m_intersections; const std::vector<std::pair<coord_t, coord_t>> &m_intersections;
Layer &m_below; Layer &m_below;
Layer &m_above; Layer &m_above;
@ -340,9 +516,9 @@ static void connect_layer_slices(
void Layer::build_up_down_graph(Layer& below, Layer& above) void Layer::build_up_down_graph(Layer& below, Layer& above)
{ {
coord_t paths_below_offset = 0; coord_t paths_below_offset = 0;
ClipperLib_Z::Paths paths_below = expolygons_to_zpaths(below.lslices, paths_below_offset); ClipperLib_Z::Paths paths_below = expolygons_to_zpaths_shrunk(below.lslices, paths_below_offset);
coord_t paths_above_offset = paths_below_offset + coord_t(below.lslices.size()); coord_t paths_above_offset = paths_below_offset + coord_t(below.lslices.size());
ClipperLib_Z::Paths paths_above = expolygons_to_zpaths(above.lslices, paths_above_offset); ClipperLib_Z::Paths paths_above = expolygons_to_zpaths_shrunk(above.lslices, paths_above_offset);
#ifndef NDEBUG #ifndef NDEBUG
coord_t paths_end = paths_above_offset + coord_t(above.lslices.size()); coord_t paths_end = paths_above_offset + coord_t(above.lslices.size());
#endif // NDEBUG #endif // NDEBUG

View File

@ -1,6 +1,7 @@
#ifndef slic3r_Layer_hpp_ #ifndef slic3r_Layer_hpp_
#define slic3r_Layer_hpp_ #define slic3r_Layer_hpp_
#include "Line.hpp"
#include "libslic3r.h" #include "libslic3r.h"
#include "BoundingBox.hpp" #include "BoundingBox.hpp"
#include "Flow.hpp" #include "Flow.hpp"
@ -325,7 +326,7 @@ public:
coordf_t bottom_z() const { return this->print_z - this->height; } coordf_t bottom_z() const { return this->print_z - this->height; }
//Extrusions estimated to be seriously malformed, estimated during "Estimating curled extrusions" step. These lines should be avoided during fast travels. //Extrusions estimated to be seriously malformed, estimated during "Estimating curled extrusions" step. These lines should be avoided during fast travels.
Lines malformed_lines; CurledLines curled_lines;
// Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry // Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry
// (with possibly differing extruder ID and slicing parameters) and merged. // (with possibly differing extruder ID and slicing parameters) and merged.

View File

@ -209,6 +209,18 @@ public:
double a_width, b_width; double a_width, b_width;
}; };
class CurledLine : public Line
{
public:
CurledLine() : curled_height(0.0f) {}
CurledLine(const Point& a, const Point& b) : Line(a, b), curled_height(0.0f) {}
CurledLine(const Point& a, const Point& b, float curled_height) : Line(a, b), curled_height(curled_height) {}
float curled_height;
};
using CurledLines = std::vector<CurledLine>;
class Line3 class Line3
{ {
public: public:

View File

@ -837,13 +837,10 @@ ModelInstance* ModelObject::add_instance(const ModelInstance &other)
return i; return i;
} }
ModelInstance* ModelObject::add_instance(const Vec3d &offset, const Vec3d &scaling_factor, const Vec3d &rotation, const Vec3d &mirror) ModelInstance* ModelObject::add_instance(const Geometry::Transformation& trafo)
{ {
auto *instance = add_instance(); ModelInstance* instance = add_instance();
instance->set_offset(offset); instance->set_transformation(trafo);
instance->set_scaling_factor(scaling_factor);
instance->set_rotation(rotation);
instance->set_mirror(mirror);
return instance; return instance;
} }

View File

@ -392,7 +392,7 @@ public:
ModelInstance* add_instance(); ModelInstance* add_instance();
ModelInstance* add_instance(const ModelInstance &instance); ModelInstance* add_instance(const ModelInstance &instance);
ModelInstance* add_instance(const Vec3d &offset, const Vec3d &scaling_factor, const Vec3d &rotation, const Vec3d &mirror); ModelInstance* add_instance(const Geometry::Transformation& trafo);
void delete_instance(size_t idx); void delete_instance(size_t idx);
void delete_last_instance(); void delete_last_instance();
void clear_instances(); void clear_instances();

View File

@ -103,9 +103,9 @@ bool MultiPoint::remove_duplicate_points()
return false; return false;
} }
std::vector<Point> MultiPoint::_douglas_peucker(const std::vector<Point>& pts, const double tolerance) Points MultiPoint::_douglas_peucker(const Points &pts, const double tolerance)
{ {
std::vector<Point> result_pts; Points result_pts;
double tolerance_sq = tolerance * tolerance; double tolerance_sq = tolerance * tolerance;
if (! pts.empty()) { if (! pts.empty()) {
const Point *anchor = &pts.front(); const Point *anchor = &pts.front();

View File

@ -110,7 +110,7 @@ public:
}; };
extern BoundingBox get_extents(const MultiPoint &mp); extern BoundingBox get_extents(const MultiPoint &mp);
extern BoundingBox get_extents_rotated(const std::vector<Point> &points, double angle); extern BoundingBox get_extents_rotated(const Points &points, double angle);
extern BoundingBox get_extents_rotated(const MultiPoint &mp, double angle); extern BoundingBox get_extents_rotated(const MultiPoint &mp, double angle);
inline double length(const Points &pts) { inline double length(const Points &pts) {

View File

@ -39,11 +39,11 @@
#include <ostream> #include <ostream>
#include <stack> #include <stack>
#include <string> #include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <ankerl/unordered_dense.h>
// #define ARACHNE_DEBUG // #define ARACHNE_DEBUG
#ifdef ARACHNE_DEBUG #ifdef ARACHNE_DEBUG
@ -569,7 +569,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator::P
size_t occurrence = 0; size_t occurrence = 0;
bool is_overhang = false; bool is_overhang = false;
}; };
std::unordered_map<Point, PointInfo, PointHash> point_occurrence; ankerl::unordered_dense::map<Point, PointInfo, PointHash> point_occurrence;
for (const ExtrusionPath &path : paths) { for (const ExtrusionPath &path : paths) {
++point_occurrence[path.polyline.first_point()].occurrence; ++point_occurrence[path.polyline.first_point()].occurrence;
++point_occurrence[path.polyline.last_point()].occurrence; ++point_occurrence[path.polyline.last_point()].occurrence;
@ -681,7 +681,7 @@ Polylines reconnect_polylines(const Polylines &polylines, double limit_distance)
if (polylines.empty()) if (polylines.empty())
return polylines; return polylines;
std::unordered_map<size_t, Polyline> connected; ankerl::unordered_dense::map<size_t, Polyline> connected;
connected.reserve(polylines.size()); connected.reserve(polylines.size());
for (size_t i = 0; i < polylines.size(); i++) { for (size_t i = 0; i < polylines.size(); i++) {
if (!polylines[i].empty()) { if (!polylines[i].empty()) {
@ -731,7 +731,7 @@ ExtrusionPaths sort_extra_perimeters(ExtrusionPaths extra_perims, int index_of_f
{ {
if (extra_perims.empty()) return {}; if (extra_perims.empty()) return {};
std::vector<std::unordered_set<size_t>> dependencies(extra_perims.size()); std::vector<ankerl::unordered_dense::set<size_t>> dependencies(extra_perims.size());
for (size_t path_idx = 0; path_idx < extra_perims.size(); path_idx++) { for (size_t path_idx = 0; path_idx < extra_perims.size(); path_idx++) {
for (size_t prev_path_idx = 0; prev_path_idx < path_idx; prev_path_idx++) { for (size_t prev_path_idx = 0; prev_path_idx < path_idx; prev_path_idx++) {
if (paths_touch(extra_perims[path_idx], extra_perims[prev_path_idx], extrusion_spacing * 1.5f)) { if (paths_touch(extra_perims[path_idx], extra_perims[prev_path_idx], extrusion_spacing * 1.5f)) {
@ -1153,11 +1153,11 @@ void PerimeterGenerator::process_arachne(
// Find topological order with constraints from extrusions_constrains. // Find topological order with constraints from extrusions_constrains.
std::vector<size_t> blocked(all_extrusions.size(), 0); // Value indicating how many extrusions it is blocking (preceding extrusions) an extrusion. std::vector<size_t> blocked(all_extrusions.size(), 0); // Value indicating how many extrusions it is blocking (preceding extrusions) an extrusion.
std::vector<std::vector<size_t>> blocking(all_extrusions.size()); // Each extrusion contains a vector of extrusions that are blocked by this extrusion. std::vector<std::vector<size_t>> blocking(all_extrusions.size()); // Each extrusion contains a vector of extrusions that are blocked by this extrusion.
std::unordered_map<const Arachne::ExtrusionLine *, size_t> map_extrusion_to_idx; ankerl::unordered_dense::map<const Arachne::ExtrusionLine *, size_t> map_extrusion_to_idx;
for (size_t idx = 0; idx < all_extrusions.size(); idx++) for (size_t idx = 0; idx < all_extrusions.size(); idx++)
map_extrusion_to_idx.emplace(all_extrusions[idx], idx); map_extrusion_to_idx.emplace(all_extrusions[idx], idx);
auto extrusions_constrains = Arachne::WallToolPaths::getRegionOrder(all_extrusions, params.config.external_perimeters_first); Arachne::WallToolPaths::ExtrusionLineSet extrusions_constrains = Arachne::WallToolPaths::getRegionOrder(all_extrusions, params.config.external_perimeters_first);
for (auto [before, after] : extrusions_constrains) { for (auto [before, after] : extrusions_constrains) {
auto after_it = map_extrusion_to_idx.find(after); auto after_it = map_extrusion_to_idx.find(after);
++blocked[after_it->second]; ++blocked[after_it->second];

View File

@ -1,6 +1,7 @@
#include "PlaceholderParser.hpp" #include "PlaceholderParser.hpp"
#include "Exception.hpp" #include "Exception.hpp"
#include "Flow.hpp" #include "Flow.hpp"
#include "Utils.hpp"
#include <cstring> #include <cstring>
#include <ctime> #include <ctime>
#include <iomanip> #include <iomanip>
@ -204,6 +205,7 @@ namespace client
explicit expr(double d, const Iterator &it_begin, const Iterator &it_end) : m_type(TYPE_DOUBLE), it_range(it_begin, it_end) { m_data.d = d; } explicit expr(double d, const Iterator &it_begin, const Iterator &it_end) : m_type(TYPE_DOUBLE), it_range(it_begin, it_end) { m_data.d = d; }
explicit expr(const char *s) : m_type(TYPE_STRING) { m_data.s = new std::string(s); } explicit expr(const char *s) : m_type(TYPE_STRING) { m_data.s = new std::string(s); }
explicit expr(const std::string &s) : m_type(TYPE_STRING) { m_data.s = new std::string(s); } explicit expr(const std::string &s) : m_type(TYPE_STRING) { m_data.s = new std::string(s); }
explicit expr(std::string &&s) : m_type(TYPE_STRING) { m_data.s = new std::string(std::move(s)); }
explicit expr(const std::string &s, const Iterator &it_begin, const Iterator &it_end) : explicit expr(const std::string &s, const Iterator &it_begin, const Iterator &it_end) :
m_type(TYPE_STRING), it_range(it_begin, it_end) { m_data.s = new std::string(s); } m_type(TYPE_STRING), it_range(it_begin, it_end) { m_data.s = new std::string(s); }
explicit expr(expr &&rhs, const Iterator &it_begin, const Iterator &it_end) : m_type(rhs.type()), it_range{ it_begin, it_end } explicit expr(expr &&rhs, const Iterator &it_begin, const Iterator &it_end) : m_type(rhs.type()), it_range{ it_begin, it_end }
@ -904,9 +906,12 @@ namespace client
const ConfigOption *opt = ctx->resolve_symbol(opt_key_str); const ConfigOption *opt = ctx->resolve_symbol(opt_key_str);
if (opt == nullptr) { if (opt == nullptr) {
// Check whether the opt_key ends with '_'. // Check whether the opt_key ends with '_'.
if (opt_key_str.back() == '_') if (opt_key_str.back() == '_') {
opt_key_str.resize(opt_key_str.size() - 1); opt_key_str.resize(opt_key_str.size() - 1);
opt = ctx->resolve_symbol(opt_key_str); opt = ctx->resolve_symbol(opt_key_str);
}
if (opt == nullptr)
ctx->throw_exception("Variable does not exist", opt_key);
} }
if (! opt->is_vector()) if (! opt->is_vector())
ctx->throw_exception("Trying to index a scalar variable", opt_key); ctx->throw_exception("Trying to index a scalar variable", opt_key);
@ -1701,7 +1706,7 @@ namespace client
// This parser is to be used inside a raw[] directive to accept a single valid UTF-8 character. // This parser is to be used inside a raw[] directive to accept a single valid UTF-8 character.
// If an invalid UTF-8 sequence is encountered, a qi::expectation_failure is thrown. // If an invalid UTF-8 sequence is encountered, a qi::expectation_failure is thrown.
struct utf8_char_skipper_parser : qi::primitive_parser<utf8_char_skipper_parser> struct utf8_char_parser : qi::primitive_parser<utf8_char_parser>
{ {
// Define the attribute type exposed by this parser component // Define the attribute type exposed by this parser component
template <typename Context, typename Iterator> template <typename Context, typename Iterator>
@ -1710,9 +1715,10 @@ namespace client
typedef wchar_t type; typedef wchar_t type;
}; };
// This function is called during the actual parsing process // This function is called during the actual parsing process to skip whitespaces.
// Also it throws if it encounters valid or invalid UTF-8 sequence.
template <typename Iterator, typename Context , typename Skipper, typename Attribute> template <typename Iterator, typename Context , typename Skipper, typename Attribute>
bool parse(Iterator& first, Iterator const& last, Context& context, Skipper const& skipper, Attribute& attr) const bool parse(Iterator &first, Iterator const &last, Context &context, Skipper const &skipper, Attribute& attr) const
{ {
// The skipper shall always be empty, any white space will be accepted. // The skipper shall always be empty, any white space will be accepted.
// skip_over(first, last, skipper); // skip_over(first, last, skipper);
@ -1762,6 +1768,38 @@ namespace client
} }
}; };
// This parser is to be used inside a raw[] directive to accept a single valid UTF-8 character.
// If an invalid UTF-8 sequence is encountered, a qi::expectation_failure is thrown.
struct ascii_char_skipper_parser : public utf8_char_parser
{
// This function is called during the actual parsing process
template <typename Iterator, typename Context, typename Skipper, typename Attribute>
bool parse(Iterator &first, Iterator const &last, Context &context, Skipper const &skipper, Attribute &attr) const
{
Iterator it = first;
// Let the UTF-8 parser throw if it encounters an invalid UTF-8 sequence.
if (! utf8_char_parser::parse(it, last, context, skipper, attr))
return false;
char c = *first;
if (it - first > 1 || c < 0)
MyContext::throw_exception("Non-ASCII7 characters are only allowed inside text blocks and string literals, not inside code blocks.", IteratorRange(first, it));
if (c == '\r' || c == '\n' || c == '\t' || c == ' ') {
// Skip the whitespaces
++ first;
return true;
} else
// Stop skipping, let this 7bit ASCII character be processed.
return false;
}
// This function is called during error handling to create a human readable string for the error context.
template <typename Context>
spirit::info what(Context&) const
{
return spirit::info("ASCII7_char");
}
};
struct FactorActions { struct FactorActions {
static void set_start_pos(Iterator &start_pos, expr &out) static void set_start_pos(Iterator &start_pos, expr &out)
{ out.it_range = IteratorRange(start_pos, start_pos); } { out.it_range = IteratorRange(start_pos, start_pos); }
@ -1790,8 +1828,49 @@ namespace client
if (ctx->skipping()) { if (ctx->skipping()) {
out.reset(); out.reset();
out.it_range = it_range; out.it_range = it_range;
} else } else {
out = expr(std::string(it_range.begin() + 1, it_range.end() - 1), it_range.begin(), it_range.end()); // Unescape the string, UTF-8 safe.
std::string s;
auto begin = std::next(it_range.begin());
auto end = std::prev(it_range.end());
assert(begin <= end);
{
// 1) Get the size of the string after unescaping.
size_t len = 0;
for (auto it = begin; it != end;) {
if (*it == '\\') {
if (++ it == end ||
(*it != 'r' && *it != 'n' && *it != '"' && *it != '\\'))
ctx->throw_exception("Invalid escape sequence", {std::prev(it), std::next(it) });
++ len;
++ it;
} else {
size_t n = get_utf8_sequence_length(&*it, end - it);
len += n;
it += n;
}
}
// and reserve the string.
s.reserve(len);
}
// 2) Copy & unescape the string.
for (auto it = begin; it != end;) {
if (*it == '\\') {
char c = *(++ it);
if (c == 'r')
c = '\r';
else if (c == 'n')
c = '\n';
s += c;
++ it;
} else {
size_t n = get_utf8_sequence_length(&*it, end - it);
s.append(&*it, n);
it += n;
}
}
out = expr(std::move(s), it_range.begin(), it_range.end());
}
} }
static void expr_(expr &value, Iterator &end_pos, expr &out) static void expr_(expr &value, Iterator &end_pos, expr &out)
{ auto begin_pos = out.it_range.begin(); out = expr(std::move(value), begin_pos, end_pos); } { auto begin_pos = out.it_range.begin(); out = expr(std::move(value), begin_pos, end_pos); }
@ -1807,11 +1886,13 @@ namespace client
static void noexpr(expr &out) { out.reset(); } static void noexpr(expr &out) { out.reset(); }
}; };
using skipper = ascii_char_skipper_parser;
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Our macro_processor grammar // Our macro_processor grammar
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Inspired by the C grammar rules https://www.lysator.liu.se/c/ANSI-C-grammar-y.html // Inspired by the C grammar rules https://www.lysator.liu.se/c/ANSI-C-grammar-y.html
struct macro_processor : qi::grammar<Iterator, std::string(const MyContext*), qi::locals<bool>, spirit_encoding::space_type> struct macro_processor : qi::grammar<Iterator, std::string(const MyContext*), qi::locals<bool>, skipper>
{ {
macro_processor() : macro_processor::base_type(start) macro_processor() : macro_processor::base_type(start)
{ {
@ -1825,7 +1906,7 @@ namespace client
qi::no_skip_type no_skip; qi::no_skip_type no_skip;
qi::real_parser<double, strict_real_policies_without_nan_inf> strict_double; qi::real_parser<double, strict_real_policies_without_nan_inf> strict_double;
spirit_encoding::char_type char_; spirit_encoding::char_type char_;
utf8_char_skipper_parser utf8char; utf8_char_parser utf8char;
spirit::bool_type bool_; spirit::bool_type bool_;
spirit::int_type int_; spirit::int_type int_;
spirit::double_type double_; spirit::double_type double_;
@ -2165,22 +2246,22 @@ namespace client
} }
// Generic expression over expr. // Generic expression over expr.
typedef qi::rule<Iterator, expr(const MyContext*), spirit_encoding::space_type> RuleExpression; typedef qi::rule<Iterator, expr(const MyContext*), skipper> RuleExpression;
// The start of the grammar. // The start of the grammar.
qi::rule<Iterator, std::string(const MyContext*), qi::locals<bool>, spirit_encoding::space_type> start; qi::rule<Iterator, std::string(const MyContext*), qi::locals<bool>, skipper> start;
// A free-form text. // A free-form text.
qi::rule<Iterator, std::string(), spirit_encoding::space_type> text; qi::rule<Iterator, std::string(), skipper> text;
// A free-form text, possibly empty, possibly containing macro expansions. // A free-form text, possibly empty, possibly containing macro expansions.
qi::rule<Iterator, std::string(const MyContext*), spirit_encoding::space_type> text_block; qi::rule<Iterator, std::string(const MyContext*), skipper> text_block;
// Statements enclosed in curely braces {} // Statements enclosed in curely braces {}
qi::rule<Iterator, std::string(const MyContext*), spirit_encoding::space_type> block, statement, macros, if_text_block, if_macros, else_macros; qi::rule<Iterator, std::string(const MyContext*), skipper> block, statement, macros, if_text_block, if_macros, else_macros;
// Legacy variable expansion of the original Slic3r, in the form of [scalar_variable] or [vector_variable_index]. // Legacy variable expansion of the original Slic3r, in the form of [scalar_variable] or [vector_variable_index].
qi::rule<Iterator, std::string(const MyContext*), spirit_encoding::space_type> legacy_variable_expansion; qi::rule<Iterator, std::string(const MyContext*), skipper> legacy_variable_expansion;
// Parsed identifier name. // Parsed identifier name.
qi::rule<Iterator, IteratorRange(), spirit_encoding::space_type> identifier; qi::rule<Iterator, IteratorRange(), skipper> identifier;
// Ternary operator (?:) over logical_or_expression. // Ternary operator (?:) over logical_or_expression.
qi::rule<Iterator, expr(const MyContext*), qi::locals<bool>, spirit_encoding::space_type> conditional_expression; qi::rule<Iterator, expr(const MyContext*), qi::locals<bool>, skipper> conditional_expression;
// Logical or over logical_and_expressions. // Logical or over logical_and_expressions.
RuleExpression logical_or_expression; RuleExpression logical_or_expression;
// Logical and over relational_expressions. // Logical and over relational_expressions.
@ -2198,27 +2279,27 @@ namespace client
// Accepting an optional parameter. // Accepting an optional parameter.
RuleExpression optional_parameter; RuleExpression optional_parameter;
// Rule to capture a regular expression enclosed in //. // Rule to capture a regular expression enclosed in //.
qi::rule<Iterator, IteratorRange(), spirit_encoding::space_type> regular_expression; qi::rule<Iterator, IteratorRange(), skipper> regular_expression;
// Evaluate boolean expression into bool. // Evaluate boolean expression into bool.
qi::rule<Iterator, bool(const MyContext*), spirit_encoding::space_type> bool_expr_eval; qi::rule<Iterator, bool(const MyContext*), skipper> bool_expr_eval;
// Reference of a scalar variable, or reference to a field of a vector variable. // Reference of a scalar variable, or reference to a field of a vector variable.
qi::rule<Iterator, OptWithPos(const MyContext*), qi::locals<OptWithPos, int>, spirit_encoding::space_type> variable_reference; qi::rule<Iterator, OptWithPos(const MyContext*), qi::locals<OptWithPos, int>, skipper> variable_reference;
// Rule to translate an identifier to a ConfigOption, or to fail. // Rule to translate an identifier to a ConfigOption, or to fail.
qi::rule<Iterator, OptWithPos(const MyContext*), spirit_encoding::space_type> variable; qi::rule<Iterator, OptWithPos(const MyContext*), skipper> variable;
// Evaluating whether a nullable variable is nil. // Evaluating whether a nullable variable is nil.
qi::rule<Iterator, expr(const MyContext*), spirit_encoding::space_type> is_nil_test; qi::rule<Iterator, expr(const MyContext*), skipper> is_nil_test;
// Evaluating "one of" list of patterns. // Evaluating "one of" list of patterns.
qi::rule<Iterator, expr(const MyContext*), qi::locals<expr>, spirit_encoding::space_type> one_of; qi::rule<Iterator, expr(const MyContext*), qi::locals<expr>, skipper> one_of;
qi::rule<Iterator, expr(const MyContext*, const expr &param), spirit_encoding::space_type> one_of_list; qi::rule<Iterator, expr(const MyContext*, const expr &param), skipper> one_of_list;
// Evaluating the "interpolate_table" expression. // Evaluating the "interpolate_table" expression.
qi::rule<Iterator, expr(const MyContext*), qi::locals<expr>, spirit_encoding::space_type> interpolate_table; qi::rule<Iterator, expr(const MyContext*), qi::locals<expr>, skipper> interpolate_table;
qi::rule<Iterator, InterpolateTableContext(const MyContext*, const expr &param), spirit_encoding::space_type> interpolate_table_list; qi::rule<Iterator, InterpolateTableContext(const MyContext*, const expr &param), skipper> interpolate_table_list;
qi::rule<Iterator, std::string(const MyContext*), qi::locals<bool>, spirit_encoding::space_type> if_else_output; qi::rule<Iterator, std::string(const MyContext*), qi::locals<bool>, skipper> if_else_output;
qi::rule<Iterator, std::string(const MyContext*), qi::locals<OptWithPos>, spirit_encoding::space_type> assignment_statement; qi::rule<Iterator, std::string(const MyContext*), qi::locals<OptWithPos>, skipper> assignment_statement;
// Allocating new local or global variables. // Allocating new local or global variables.
qi::rule<Iterator, std::string(const MyContext*), qi::locals<bool, MyContext::NewOldVariable>, spirit_encoding::space_type> new_variable_statement; qi::rule<Iterator, std::string(const MyContext*), qi::locals<bool, MyContext::NewOldVariable>, skipper> new_variable_statement;
qi::rule<Iterator, std::vector<expr>(const MyContext*), spirit_encoding::space_type> initializer_list; qi::rule<Iterator, std::vector<expr>(const MyContext*), skipper> initializer_list;
qi::symbols<char> keywords; qi::symbols<char> keywords;
}; };
@ -2229,7 +2310,7 @@ static const client::macro_processor g_macro_processor_instance;
static std::string process_macro(const std::string &templ, client::MyContext &context) static std::string process_macro(const std::string &templ, client::MyContext &context)
{ {
std::string output; std::string output;
phrase_parse(templ.begin(), templ.end(), g_macro_processor_instance(&context), spirit_encoding::space_type{}, output); phrase_parse(templ.begin(), templ.end(), g_macro_processor_instance(&context), client::skipper{}, output);
if (! context.error_message.empty()) { if (! context.error_message.empty()) {
if (context.error_message.back() != '\n' && context.error_message.back() != '\r') if (context.error_message.back() != '\n' && context.error_message.back() != '\r')
context.error_message += '\n'; context.error_message += '\n';

View File

@ -57,7 +57,7 @@ void Point::rotate(double angle, const Point &center)
(*this)(1) = (coord_t)round( (double)center(1) + c * dy + s * dx ); (*this)(1) = (coord_t)round( (double)center(1) + c * dy + s * dx );
} }
bool has_duplicate_points(std::vector<Point> &&pts) bool has_duplicate_points(Points &&pts)
{ {
std::sort(pts.begin(), pts.end()); std::sort(pts.begin(), pts.end());
for (size_t i = 1; i < pts.size(); ++ i) for (size_t i = 1; i < pts.size(); ++ i)
@ -97,15 +97,15 @@ template BoundingBox get_extents<true>(const Points &pts);
// if IncludeBoundary, then a bounding box is defined even for a single point. // if IncludeBoundary, then a bounding box is defined even for a single point.
// otherwise a bounding box is only defined if it has a positive area. // otherwise a bounding box is only defined if it has a positive area.
template<bool IncludeBoundary> template<bool IncludeBoundary>
BoundingBox get_extents(const std::vector<Points> &pts) BoundingBox get_extents(const VecOfPoints &pts)
{ {
BoundingBox bbox; BoundingBox bbox;
for (const Points &p : pts) for (const Points &p : pts)
bbox.merge(get_extents<IncludeBoundary>(p)); bbox.merge(get_extents<IncludeBoundary>(p));
return bbox; return bbox;
} }
template BoundingBox get_extents<false>(const std::vector<Points> &pts); template BoundingBox get_extents<false>(const VecOfPoints &pts);
template BoundingBox get_extents<true>(const std::vector<Points> &pts); template BoundingBox get_extents<true>(const VecOfPoints &pts);
BoundingBoxf get_extents(const std::vector<Vec2d> &pts) BoundingBoxf get_extents(const std::vector<Vec2d> &pts)
{ {

View File

@ -9,6 +9,9 @@
#include <sstream> #include <sstream>
#include <unordered_map> #include <unordered_map>
#include <oneapi/tbb/scalable_allocator.h>
#include <Eigen/Geometry> #include <Eigen/Geometry>
#include "LocalesUtils.hpp" #include "LocalesUtils.hpp"
@ -49,7 +52,10 @@ using Vec2d = Eigen::Matrix<double, 2, 1, Eigen::DontAlign>;
using Vec3d = Eigen::Matrix<double, 3, 1, Eigen::DontAlign>; using Vec3d = Eigen::Matrix<double, 3, 1, Eigen::DontAlign>;
using Vec4d = Eigen::Matrix<double, 4, 1, Eigen::DontAlign>; using Vec4d = Eigen::Matrix<double, 4, 1, Eigen::DontAlign>;
using Points = std::vector<Point>; template<typename BaseType>
using PointsAllocator = tbb::scalable_allocator<BaseType>;
//using PointsAllocator = std::allocator<BaseType>;
using Points = std::vector<Point, PointsAllocator<Point>>;
using PointPtrs = std::vector<Point*>; using PointPtrs = std::vector<Point*>;
using PointConstPtrs = std::vector<const Point*>; using PointConstPtrs = std::vector<const Point*>;
using Points3 = std::vector<Vec3crd>; using Points3 = std::vector<Vec3crd>;
@ -57,6 +63,8 @@ using Pointfs = std::vector<Vec2d>;
using Vec2ds = std::vector<Vec2d>; using Vec2ds = std::vector<Vec2d>;
using Pointf3s = std::vector<Vec3d>; using Pointf3s = std::vector<Vec3d>;
using VecOfPoints = std::vector<Points, PointsAllocator<Points>>;
using Matrix2f = Eigen::Matrix<float, 2, 2, Eigen::DontAlign>; using Matrix2f = Eigen::Matrix<float, 2, 2, Eigen::DontAlign>;
using Matrix2d = Eigen::Matrix<double, 2, 2, Eigen::DontAlign>; using Matrix2d = Eigen::Matrix<double, 2, 2, Eigen::DontAlign>;
using Matrix3f = Eigen::Matrix<float, 3, 3, Eigen::DontAlign>; using Matrix3f = Eigen::Matrix<float, 3, 3, Eigen::DontAlign>;
@ -247,9 +255,9 @@ extern template BoundingBox get_extents<true>(const Points &pts);
// if IncludeBoundary, then a bounding box is defined even for a single point. // if IncludeBoundary, then a bounding box is defined even for a single point.
// otherwise a bounding box is only defined if it has a positive area. // otherwise a bounding box is only defined if it has a positive area.
template<bool IncludeBoundary = false> template<bool IncludeBoundary = false>
BoundingBox get_extents(const std::vector<Points> &pts); BoundingBox get_extents(const VecOfPoints &pts);
extern template BoundingBox get_extents<false>(const std::vector<Points> &pts); extern template BoundingBox get_extents<false>(const VecOfPoints &pts);
extern template BoundingBox get_extents<true>(const std::vector<Points> &pts); extern template BoundingBox get_extents<true>(const VecOfPoints &pts);
BoundingBoxf get_extents(const std::vector<Vec2d> &pts); BoundingBoxf get_extents(const std::vector<Vec2d> &pts);
@ -263,16 +271,16 @@ inline std::pair<Point, bool> nearest_point(const Points &points, const Point &p
// Test for duplicate points in a vector of points. // Test for duplicate points in a vector of points.
// The points are copied, sorted and checked for duplicates globally. // The points are copied, sorted and checked for duplicates globally.
bool has_duplicate_points(std::vector<Point> &&pts); bool has_duplicate_points(Points &&pts);
inline bool has_duplicate_points(const std::vector<Point> &pts) inline bool has_duplicate_points(const Points &pts)
{ {
std::vector<Point> cpy = pts; Points cpy = pts;
return has_duplicate_points(std::move(cpy)); return has_duplicate_points(std::move(cpy));
} }
// Test for duplicate points in a vector of points. // Test for duplicate points in a vector of points.
// Only successive points are checked for equality. // Only successive points are checked for equality.
inline bool has_duplicate_successive_points(const std::vector<Point> &pts) inline bool has_duplicate_successive_points(const Points &pts)
{ {
for (size_t i = 1; i < pts.size(); ++ i) for (size_t i = 1; i < pts.size(); ++ i)
if (pts[i - 1] == pts[i]) if (pts[i - 1] == pts[i])
@ -282,7 +290,7 @@ inline bool has_duplicate_successive_points(const std::vector<Point> &pts)
// Test for duplicate points in a vector of points. // Test for duplicate points in a vector of points.
// Only successive points are checked for equality. Additionally, first and last points are compared for equality. // Only successive points are checked for equality. Additionally, first and last points are compared for equality.
inline bool has_duplicate_successive_points_closed(const std::vector<Point> &pts) inline bool has_duplicate_successive_points_closed(const Points &pts)
{ {
return has_duplicate_successive_points(pts) || (pts.size() >= 2 && pts.front() == pts.back()); return has_duplicate_successive_points(pts) || (pts.size() >= 2 && pts.front() == pts.back());
} }

View File

@ -4,6 +4,8 @@
#include "Polygon.hpp" #include "Polygon.hpp"
#include "Polyline.hpp" #include "Polyline.hpp"
#include <ankerl/unordered_dense.h>
namespace Slic3r { namespace Slic3r {
double Polygon::length() const double Polygon::length() const
@ -400,14 +402,32 @@ bool has_duplicate_points(const Polygons &polys)
{ {
#if 1 #if 1
// Check globally. // Check globally.
size_t cnt = 0; #if 0
for (const Polygon &poly : polys) // Detect duplicates by sorting with quicksort. It is quite fast, but ankerl::unordered_dense is around 1/4 faster.
cnt += poly.points.size(); Points allpts;
std::vector<Point> allpts; allpts.reserve(count_points(polys));
allpts.reserve(cnt);
for (const Polygon &poly : polys) for (const Polygon &poly : polys)
allpts.insert(allpts.end(), poly.points.begin(), poly.points.end()); allpts.insert(allpts.end(), poly.points.begin(), poly.points.end());
return has_duplicate_points(std::move(allpts)); return has_duplicate_points(std::move(allpts));
#else
// Detect duplicates by inserting into an ankerl::unordered_dense hash set, which is is around 1/4 faster than qsort.
struct PointHash {
uint64_t operator()(const Point &p) const noexcept {
uint64_t h;
static_assert(sizeof(h) == sizeof(p));
memcpy(&h, &p, sizeof(p));
return ankerl::unordered_dense::detail::wyhash::hash(h);
}
};
ankerl::unordered_dense::set<Point, PointHash> allpts;
allpts.reserve(count_points(polys));
for (const Polygon &poly : polys)
for (const Point &pt : poly.points)
if (! allpts.insert(pt).second)
// Duplicate point was discovered.
return true;
return false;
#endif
#else #else
// Check per contour. // Check per contour.
for (const Polygon &poly : polys) for (const Polygon &poly : polys)

View File

@ -5,15 +5,16 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include "Line.hpp" #include "Line.hpp"
#include "Point.hpp"
#include "MultiPoint.hpp" #include "MultiPoint.hpp"
#include "Polyline.hpp" #include "Polyline.hpp"
namespace Slic3r { namespace Slic3r {
class Polygon; class Polygon;
using Polygons = std::vector<Polygon>; using Polygons = std::vector<Polygon, PointsAllocator<Polygon>>;
using PolygonPtrs = std::vector<Polygon*>; using PolygonPtrs = std::vector<Polygon*, PointsAllocator<Polygon*>>;
using ConstPolygonPtrs = std::vector<const Polygon*>; using ConstPolygonPtrs = std::vector<const Polygon*, PointsAllocator<const Polygon*>>;
// Returns true if inside. Returns border_result if on boundary. // Returns true if inside. Returns border_result if on boundary.
bool contains(const Polygon& polygon, const Point& p, bool border_result = true); bool contains(const Polygon& polygon, const Point& p, bool border_result = true);
@ -241,7 +242,7 @@ inline Polylines to_polylines(Polygons &&polys)
return polylines; return polylines;
} }
inline Polygons to_polygons(const std::vector<Points> &paths) inline Polygons to_polygons(const VecOfPoints &paths)
{ {
Polygons out; Polygons out;
out.reserve(paths.size()); out.reserve(paths.size());
@ -250,7 +251,7 @@ inline Polygons to_polygons(const std::vector<Points> &paths)
return out; return out;
} }
inline Polygons to_polygons(std::vector<Points> &&paths) inline Polygons to_polygons(VecOfPoints &&paths)
{ {
Polygons out; Polygons out;
out.reserve(paths.size()); out.reserve(paths.size());

View File

@ -17,7 +17,7 @@ namespace EdgeGrid {
struct TrimmedLoop struct TrimmedLoop
{ {
std::vector<Point> points; Points points;
// Number of points per segment. Empty if the loop is // Number of points per segment. Empty if the loop is
std::vector<unsigned int> segments; std::vector<unsigned int> segments;

View File

@ -158,8 +158,8 @@ inline void polylines_append(Polylines &dst, Polylines &&src)
// src_first: the merge point is at src.begin() or src.end()? // src_first: the merge point is at src.begin() or src.end()?
// The orientation of the resulting polyline is unknown, the output polyline may start // The orientation of the resulting polyline is unknown, the output polyline may start
// either with src piece or dst piece. // either with src piece or dst piece.
template<typename PointType> template<typename PointsType>
inline void polylines_merge(std::vector<PointType> &dst, bool dst_first, std::vector<PointType> &&src, bool src_first) inline void polylines_merge(PointsType &dst, bool dst_first, PointsType &&src, bool src_first)
{ {
if (dst_first) { if (dst_first) {
if (src_first) if (src_first)

View File

@ -518,8 +518,12 @@ std::string Print::validate(std::string* warning) const
//FIXME It is quite expensive to generate object layers just to get the print height! //FIXME It is quite expensive to generate object layers just to get the print height!
if (auto layers = generate_object_layers(print_object.slicing_parameters(), layer_height_profile(print_object_idx)); if (auto layers = generate_object_layers(print_object.slicing_parameters(), layer_height_profile(print_object_idx));
! layers.empty() && layers.back() > this->config().max_print_height + EPSILON) { ! layers.empty() && layers.back() > this->config().max_print_height + EPSILON) {
return _u8L("The print is taller than the maximum allowed height. You might want to reduce the size of your model" return
" or change current print settings and retry."); // Test whether the last slicing plane is below or above the print volume.
0.5 * (layers[layers.size() - 2] + layers.back()) > this->config().max_print_height + EPSILON ?
format(_u8L("The object %1% exceeds the maximum build volume height."), print_object.model_object()->name) :
format(_u8L("While the object %1% itself fits the build volume, its last layer exceeds the maximum build volume height."), print_object.model_object()->name) +
" " + _u8L("You might want to reduce the size of your model or change current print settings and retry.");
} }
} }
@ -1128,9 +1132,9 @@ Polygons Print::first_layer_islands() const
return islands; return islands;
} }
std::vector<Point> Print::first_layer_wipe_tower_corners() const Points Print::first_layer_wipe_tower_corners() const
{ {
std::vector<Point> pts_scaled; Points pts_scaled;
if (has_wipe_tower() && ! m_wipe_tower_data.tool_changes.empty()) { if (has_wipe_tower() && ! m_wipe_tower_data.tool_changes.empty()) {
double width = m_config.wipe_tower_width + 2*m_wipe_tower_data.brim_width; double width = m_config.wipe_tower_width + 2*m_wipe_tower_data.brim_width;

View File

@ -626,7 +626,7 @@ private:
// Islands of objects and their supports extruded at the 1st layer. // Islands of objects and their supports extruded at the 1st layer.
Polygons first_layer_islands() const; Polygons first_layer_islands() const;
// Return 4 wipe tower corners in the world coordinates (shifted and rotated), including the wipe tower brim. // Return 4 wipe tower corners in the world coordinates (shifted and rotated), including the wipe tower brim.
std::vector<Point> first_layer_wipe_tower_corners() const; Points first_layer_wipe_tower_corners() const;
// Returns true if any of the print_objects has print_object_step valid. // Returns true if any of the print_objects has print_object_step valid.
// That means data shared by all print objects of the print_objects span may still use the shared data. // That means data shared by all print objects of the print_objects span may still use the shared data.
@ -660,6 +660,8 @@ private:
// To allow GCode to set the Print's GCodeExport step status. // To allow GCode to set the Print's GCodeExport step status.
friend class GCode; friend class GCode;
// To allow GCodeProcessor to emit warnings.
friend class GCodeProcessor;
// Allow PrintObject to access m_mutex and m_cancel_callback. // Allow PrintObject to access m_mutex and m_cancel_callback.
friend class PrintObject; friend class PrintObject;
}; };

View File

@ -2908,7 +2908,8 @@ void PrintConfigDef::init_fff_params()
// TRN PrintSettings: "Organic supports" > "Tip Diameter" // TRN PrintSettings: "Organic supports" > "Tip Diameter"
def->tooltip = L("Branch tip diameter for organic supports."); def->tooltip = L("Branch tip diameter for organic supports.");
def->sidetext = L("mm"); def->sidetext = L("mm");
def->min = 0; def->min = 0.1f;
def->max = 100.f;
def->mode = comAdvanced; def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.8)); def->set_default_value(new ConfigOptionFloat(0.8));
@ -2919,7 +2920,8 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("The diameter of the thinnest branches of organic support. Thicker branches are more sturdy. " def->tooltip = L("The diameter of the thinnest branches of organic support. Thicker branches are more sturdy. "
"Branches towards the base will be thicker than this."); "Branches towards the base will be thicker than this.");
def->sidetext = L("mm"); def->sidetext = L("mm");
def->min = 0; def->min = 0.1f;
def->max = 100.f;
def->mode = comAdvanced; def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(2)); def->set_default_value(new ConfigOptionFloat(2));
@ -3867,7 +3869,9 @@ void PrintConfigDef::init_sla_params()
def->multiline = true; def->multiline = true;
def->full_width = true; def->full_width = true;
def->height = 13; def->height = 13;
def->mode = comAdvanced; // TODO currently notes are the only way to pass data
// for non-PrusaResearch printers. We therefore need to always show them
def->mode = comSimple;
def->set_default_value(new ConfigOptionString("")); def->set_default_value(new ConfigOptionString(""));
def = this->add("material_vendor", coString); def = this->add("material_vendor", coString);

View File

@ -44,7 +44,7 @@ enum class MachineLimitsUsage {
}; };
enum PrintHostType { enum PrintHostType {
htPrusaLink, htPrusaConnect, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS, htMainSail htPrusaLink, htPrusaConnect, htOctoPrint, htMainSail, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS
}; };
enum AuthorizationType { enum AuthorizationType {

View File

@ -56,6 +56,20 @@
using namespace std::literals; using namespace std::literals;
// #define PRINT_OBJECT_TIMING
#ifdef PRINT_OBJECT_TIMING
// time limit for one ClipperLib operation (union / diff / offset), in ms
#define PRINT_OBJECT_TIME_LIMIT_DEFAULT 50
#include <boost/current_function.hpp>
#include "Timer.hpp"
#define PRINT_OBJECT_TIME_LIMIT_SECONDS(limit) Timing::TimeLimitAlarm time_limit_alarm(uint64_t(limit) * 1000000000l, BOOST_CURRENT_FUNCTION)
#define PRINT_OBJECT_TIME_LIMIT_MILLIS(limit) Timing::TimeLimitAlarm time_limit_alarm(uint64_t(limit) * 1000000l, BOOST_CURRENT_FUNCTION)
#else
#define PRINT_OBJECT_TIME_LIMIT_SECONDS(limit) do {} while(false)
#define PRINT_OBJECT_TIME_LIMIT_MILLIS(limit) do {} while(false)
#endif // PRINT_OBJECT_TIMING
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
#define SLIC3R_DEBUG #define SLIC3R_DEBUG
#endif #endif
@ -102,7 +116,7 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, const Transfor
m_center_offset = Point::new_scale(bbox_center.x(), bbox_center.y()); m_center_offset = Point::new_scale(bbox_center.x(), bbox_center.y());
// Size of the transformed mesh. This bounding may not be snug in XY plane, but it is snug in Z. // Size of the transformed mesh. This bounding may not be snug in XY plane, but it is snug in Z.
m_size = (bbox.size() * (1. / SCALING_FACTOR)).cast<coord_t>(); m_size = (bbox.size() * (1. / SCALING_FACTOR)).cast<coord_t>();
m_size.z() = model_object->max_z(); m_size.z() = coord_t(model_object->max_z() * (1. / SCALING_FACTOR));
this->set_instances(std::move(instances)); this->set_instances(std::move(instances));
} }
@ -178,6 +192,7 @@ void PrintObject::make_perimeters()
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size() - 1), tbb::blocked_range<size_t>(0, m_layers.size() - 1),
[this, &region, region_id](const tbb::blocked_range<size_t>& range) { [this, &region, region_id](const tbb::blocked_range<size_t>& range) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
m_print->throw_if_canceled(); m_print->throw_if_canceled();
LayerRegion &layerm = *m_layers[layer_idx]->get_region(region_id); LayerRegion &layerm = *m_layers[layer_idx]->get_region(region_id);
@ -237,6 +252,7 @@ void PrintObject::make_perimeters()
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()), tbb::blocked_range<size_t>(0, m_layers.size()),
[this](const tbb::blocked_range<size_t>& range) { [this](const tbb::blocked_range<size_t>& range) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
m_print->throw_if_canceled(); m_print->throw_if_canceled();
m_layers[layer_idx]->make_perimeters(); m_layers[layer_idx]->make_perimeters();
@ -408,6 +424,7 @@ void PrintObject::infill()
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()), tbb::blocked_range<size_t>(0, m_layers.size()),
[this, &adaptive_fill_octree = adaptive_fill_octree, &support_fill_octree = support_fill_octree](const tbb::blocked_range<size_t>& range) { [this, &adaptive_fill_octree = adaptive_fill_octree, &support_fill_octree = support_fill_octree](const tbb::blocked_range<size_t>& range) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
m_print->throw_if_canceled(); m_print->throw_if_canceled();
m_layers[layer_idx]->make_fills(adaptive_fill_octree.get(), support_fill_octree.get(), this->m_lightning_generator.get()); m_layers[layer_idx]->make_fills(adaptive_fill_octree.get(), support_fill_octree.get(), this->m_lightning_generator.get());
@ -431,6 +448,7 @@ void PrintObject::ironing()
// Ironing starting with layer 0 to support ironing all surfaces. // Ironing starting with layer 0 to support ironing all surfaces.
tbb::blocked_range<size_t>(0, m_layers.size()), tbb::blocked_range<size_t>(0, m_layers.size()),
[this](const tbb::blocked_range<size_t>& range) { [this](const tbb::blocked_range<size_t>& range) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
m_print->throw_if_canceled(); m_print->throw_if_canceled();
m_layers[layer_idx]->make_ironing(); m_layers[layer_idx]->make_ironing();
@ -491,7 +509,9 @@ void PrintObject::generate_support_material()
void PrintObject::estimate_curled_extrusions() void PrintObject::estimate_curled_extrusions()
{ {
if (this->set_started(posEstimateCurledExtrusions)) { if (this->set_started(posEstimateCurledExtrusions)) {
if (this->print()->config().avoid_crossing_curled_overhangs) { if (this->print()->config().avoid_crossing_curled_overhangs ||
std::any_of(this->print()->m_print_regions.begin(), this->print()->m_print_regions.end(),
[](const PrintRegion *region) { return region->config().enable_dynamic_overhang_speeds.getBool(); })) {
BOOST_LOG_TRIVIAL(debug) << "Estimating areas with curled extrusions - start"; BOOST_LOG_TRIVIAL(debug) << "Estimating areas with curled extrusions - start";
m_print->set_status(88, _u8L("Estimating curled extrusions")); m_print->set_status(88, _u8L("Estimating curled extrusions"));
@ -528,16 +548,17 @@ std::pair<FillAdaptive::OctreePtr, FillAdaptive::OctreePtr> PrintObject::prepare
std::vector<std::vector<Vec3d>> overhangs(std::max(surfaces_w_bottom_z.size(), size_t(1))); std::vector<std::vector<Vec3d>> overhangs(std::max(surfaces_w_bottom_z.size(), size_t(1)));
// ^ make sure vector is not empty, even with no briding surfaces we still want to build the adaptive trees later, some continue normally // ^ make sure vector is not empty, even with no briding surfaces we still want to build the adaptive trees later, some continue normally
tbb::parallel_for(tbb::blocked_range<int>(0, surfaces_w_bottom_z.size()), tbb::parallel_for(tbb::blocked_range<int>(0, surfaces_w_bottom_z.size()),
[this, &to_octree, &overhangs, &surfaces_w_bottom_z](const tbb::blocked_range<int> &range) { [this, &to_octree, &overhangs, &surfaces_w_bottom_z](const tbb::blocked_range<int> &range) {
for (int surface_idx = range.begin(); surface_idx < range.end(); ++surface_idx) { PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
std::vector<Vec3d> &out = overhangs[surface_idx]; for (int surface_idx = range.begin(); surface_idx < range.end(); ++surface_idx) {
m_print->throw_if_canceled(); std::vector<Vec3d> &out = overhangs[surface_idx];
append(out, triangulate_expolygon_3d(surfaces_w_bottom_z[surface_idx].first->expolygon, m_print->throw_if_canceled();
surfaces_w_bottom_z[surface_idx].second)); append(out, triangulate_expolygon_3d(surfaces_w_bottom_z[surface_idx].first->expolygon,
for (Vec3d &p : out) surfaces_w_bottom_z[surface_idx].second));
p = (to_octree * p).eval(); for (Vec3d &p : out)
} p = (to_octree * p).eval();
}); }
});
// and gather them. // and gather them.
for (size_t i = 1; i < overhangs.size(); ++ i) for (size_t i = 1; i < overhangs.size(); ++ i)
append(overhangs.front(), std::move(overhangs[i])); append(overhangs.front(), std::move(overhangs[i]));
@ -909,6 +930,7 @@ void PrintObject::detect_surfaces_type()
// In non-spiral vase mode, go over all layers. // In non-spiral vase mode, go over all layers.
m_layers.size()), m_layers.size()),
[this, region_id, interface_shells, &surfaces_new](const tbb::blocked_range<size_t>& range) { [this, region_id, interface_shells, &surfaces_new](const tbb::blocked_range<size_t>& range) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
// If we have soluble support material, don't bridge. The overhang will be squished against a soluble layer separating // If we have soluble support material, don't bridge. The overhang will be squished against a soluble layer separating
// the support from the print. // the support from the print.
SurfaceType surface_type_bottom_other = SurfaceType surface_type_bottom_other =
@ -1057,6 +1079,7 @@ void PrintObject::detect_surfaces_type()
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()), tbb::blocked_range<size_t>(0, m_layers.size()),
[this, region_id](const tbb::blocked_range<size_t>& range) { [this, region_id](const tbb::blocked_range<size_t>& range) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
m_print->throw_if_canceled(); m_print->throw_if_canceled();
LayerRegion *layerm = m_layers[idx_layer]->m_regions[region_id]; LayerRegion *layerm = m_layers[idx_layer]->m_regions[region_id];
@ -1115,6 +1138,7 @@ void PrintObject::process_external_surfaces()
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size() - 1), tbb::blocked_range<size_t>(0, m_layers.size() - 1),
[this, &surfaces_covered, &layer_expansions_and_voids, unsupported_width](const tbb::blocked_range<size_t>& range) { [this, &surfaces_covered, &layer_expansions_and_voids, unsupported_width](const tbb::blocked_range<size_t>& range) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx)
if (layer_expansions_and_voids[layer_idx + 1]) { if (layer_expansions_and_voids[layer_idx + 1]) {
// Layer above is partially filled with solid infill (top, bottom, bridging...), // Layer above is partially filled with solid infill (top, bottom, bridging...),
@ -1140,6 +1164,7 @@ void PrintObject::process_external_surfaces()
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()), tbb::blocked_range<size_t>(0, m_layers.size()),
[this, &surfaces_covered, region_id](const tbb::blocked_range<size_t>& range) { [this, &surfaces_covered, region_id](const tbb::blocked_range<size_t>& range) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
m_print->throw_if_canceled(); m_print->throw_if_canceled();
// BOOST_LOG_TRIVIAL(trace) << "Processing external surface, layer" << m_layers[layer_idx]->print_z; // BOOST_LOG_TRIVIAL(trace) << "Processing external surface, layer" << m_layers[layer_idx]->print_z;
@ -1178,19 +1203,6 @@ void PrintObject::discover_vertical_shells()
}; };
bool spiral_vase = this->print()->config().spiral_vase.value; bool spiral_vase = this->print()->config().spiral_vase.value;
size_t num_layers = spiral_vase ? std::min(size_t(this->printing_region(0).config().bottom_solid_layers), m_layers.size()) : m_layers.size(); size_t num_layers = spiral_vase ? std::min(size_t(this->printing_region(0).config().bottom_solid_layers), m_layers.size()) : m_layers.size();
coordf_t min_layer_height = this->slicing_parameters().min_layer_height;
// Does this region possibly produce more than 1 top or bottom layer?
auto has_extra_layers_fn = [min_layer_height](const PrintRegionConfig &config) {
auto num_extra_layers = [min_layer_height](int num_solid_layers, coordf_t min_shell_thickness) {
if (num_solid_layers == 0)
return 0;
int n = num_solid_layers - 1;
int n2 = int(ceil(min_shell_thickness / min_layer_height));
return std::max(n, n2 - 1);
};
return num_extra_layers(config.top_solid_layers, config.top_solid_min_thickness) +
num_extra_layers(config.bottom_solid_layers, config.bottom_solid_min_thickness) > 0;
};
std::vector<DiscoverVerticalShellsCacheEntry> cache_top_botom_regions(num_layers, DiscoverVerticalShellsCacheEntry()); std::vector<DiscoverVerticalShellsCacheEntry> cache_top_botom_regions(num_layers, DiscoverVerticalShellsCacheEntry());
bool top_bottom_surfaces_all_regions = this->num_printing_regions() > 1 && ! m_config.interface_shells.value; bool top_bottom_surfaces_all_regions = this->num_printing_regions() > 1 && ! m_config.interface_shells.value;
// static constexpr const float top_bottom_expansion_coeff = 1.05f; // static constexpr const float top_bottom_expansion_coeff = 1.05f;
@ -1199,24 +1211,13 @@ void PrintObject::discover_vertical_shells()
if (top_bottom_surfaces_all_regions) { if (top_bottom_surfaces_all_regions) {
// This is a multi-material print and interface_shells are disabled, meaning that the vertical shell thickness // This is a multi-material print and interface_shells are disabled, meaning that the vertical shell thickness
// is calculated over all materials. // is calculated over all materials.
// Is the "ensure vertical wall thickness" applicable to any region?
bool has_extra_layers = false;
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) {
const PrintRegionConfig &config = this->printing_region(region_id).config();
if (has_extra_layers_fn(config)) {
has_extra_layers = true;
break;
}
}
if (! has_extra_layers)
// The "ensure vertical wall thickness" feature is not applicable to any of the regions. Quit.
return;
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - start : cache top / bottom"; BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - start : cache top / bottom";
//FIXME Improve the heuristics for a grain size. //FIXME Improve the heuristics for a grain size.
size_t grain_size = std::max(num_layers / 16, size_t(1)); size_t grain_size = std::max(num_layers / 16, size_t(1));
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, num_layers, grain_size), tbb::blocked_range<size_t>(0, num_layers, grain_size),
[this, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) { [this, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
const std::initializer_list<SurfaceType> surfaces_bottom { stBottom, stBottomBridge }; const std::initializer_list<SurfaceType> surfaces_bottom { stBottom, stBottomBridge };
const size_t num_regions = this->num_printing_regions(); const size_t num_regions = this->num_printing_regions();
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
@ -1280,11 +1281,6 @@ void PrintObject::discover_vertical_shells()
} }
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
const PrintRegion &region = this->printing_region(region_id);
if (! has_extra_layers_fn(region.config()))
// Zero or 1 layer, there is no additional vertical wall thickness enforced.
continue;
//FIXME Improve the heuristics for a grain size. //FIXME Improve the heuristics for a grain size.
size_t grain_size = std::max(num_layers / 16, size_t(1)); size_t grain_size = std::max(num_layers / 16, size_t(1));
@ -1295,6 +1291,7 @@ void PrintObject::discover_vertical_shells()
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, num_layers, grain_size), tbb::blocked_range<size_t>(0, num_layers, grain_size),
[this, region_id, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) { [this, region_id, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
const std::initializer_list<SurfaceType> surfaces_bottom { stBottom, stBottomBridge }; const std::initializer_list<SurfaceType> surfaces_bottom { stBottom, stBottomBridge };
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
m_print->throw_if_canceled(); m_print->throw_if_canceled();
@ -1320,10 +1317,12 @@ void PrintObject::discover_vertical_shells()
} }
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << region_id << " in parallel - start : ensure vertical wall thickness"; BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << region_id << " in parallel - start : ensure vertical wall thickness";
grain_size = 1;
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, num_layers, grain_size), tbb::blocked_range<size_t>(0, num_layers, grain_size),
[this, region_id, &cache_top_botom_regions] [this, region_id, &cache_top_botom_regions]
(const tbb::blocked_range<size_t>& range) { (const tbb::blocked_range<size_t>& range) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
// printf("discover_vertical_shells from %d to %d\n", range.begin(), range.end()); // printf("discover_vertical_shells from %d to %d\n", range.begin(), range.end());
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
m_print->throw_if_canceled(); m_print->throw_if_canceled();
@ -1395,13 +1394,25 @@ void PrintObject::discover_vertical_shells()
coordf_t print_z = layer->print_z; coordf_t print_z = layer->print_z;
int i = int(idx_layer) + 1; int i = int(idx_layer) + 1;
int itop = int(idx_layer) + n_top_layers; int itop = int(idx_layer) + n_top_layers;
bool at_least_one_top_projected = false;
for (; i < int(cache_top_botom_regions.size()) && for (; i < int(cache_top_botom_regions.size()) &&
(i < itop || m_layers[i]->print_z - print_z < region_config.top_solid_min_thickness - EPSILON); (i < itop || m_layers[i]->print_z - print_z < region_config.top_solid_min_thickness - EPSILON);
++ i) { ++ i) {
at_least_one_top_projected = true;
const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i]; const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i];
combine_holes(cache.holes); combine_holes(cache.holes);
combine_shells(cache.top_surfaces); combine_shells(cache.top_surfaces);
} }
if (!at_least_one_top_projected && i < int(cache_top_botom_regions.size())) {
// Lets consider this a special case - with only 1 top solid and minimal shell thickness settings, the
// boundaries of solid layers are not anchored over/under perimeters, so lets fix it by adding at least one
// perimeter width of area
Polygons anchor_area = intersection(expand(cache_top_botom_regions[idx_layer].top_surfaces,
layerm->flow(frExternalPerimeter).scaled_spacing()),
to_polygons(m_layers[i]->lslices));
combine_shells(anchor_area);
}
if (one_more_layer_below_top_bottom_surfaces) if (one_more_layer_below_top_bottom_surfaces)
if (i < int(cache_top_botom_regions.size()) && if (i < int(cache_top_botom_regions.size()) &&
(i <= itop || m_layers[i]->bottom_z() - print_z < region_config.top_solid_min_thickness - EPSILON)) (i <= itop || m_layers[i]->bottom_z() - print_z < region_config.top_solid_min_thickness - EPSILON))
@ -1412,13 +1423,23 @@ void PrintObject::discover_vertical_shells()
coordf_t bottom_z = layer->bottom_z(); coordf_t bottom_z = layer->bottom_z();
int i = int(idx_layer) - 1; int i = int(idx_layer) - 1;
int ibottom = int(idx_layer) - n_bottom_layers; int ibottom = int(idx_layer) - n_bottom_layers;
bool at_least_one_bottom_projected = false;
for (; i >= 0 && for (; i >= 0 &&
(i > ibottom || bottom_z - m_layers[i]->bottom_z() < region_config.bottom_solid_min_thickness - EPSILON); (i > ibottom || bottom_z - m_layers[i]->bottom_z() < region_config.bottom_solid_min_thickness - EPSILON);
-- i) { -- i) {
at_least_one_bottom_projected = true;
const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i]; const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i];
combine_holes(cache.holes); combine_holes(cache.holes);
combine_shells(cache.bottom_surfaces); combine_shells(cache.bottom_surfaces);
} }
if (!at_least_one_bottom_projected && i >= 0) {
Polygons anchor_area = intersection(expand(cache_top_botom_regions[idx_layer].bottom_surfaces,
layerm->flow(frExternalPerimeter).scaled_spacing()),
to_polygons(m_layers[i]->lslices));
combine_shells(anchor_area);
}
if (one_more_layer_below_top_bottom_surfaces) if (one_more_layer_below_top_bottom_surfaces)
if (i >= 0 && if (i >= 0 &&
(i > ibottom || bottom_z - m_layers[i]->print_z < region_config.bottom_solid_min_thickness - EPSILON)) (i > ibottom || bottom_z - m_layers[i]->print_z < region_config.bottom_solid_min_thickness - EPSILON))
@ -1514,10 +1535,21 @@ void PrintObject::discover_vertical_shells()
// Finally expand the infill a bit to remove tiny gaps between solid infill and the other regions. // Finally expand the infill a bit to remove tiny gaps between solid infill and the other regions.
narrow_sparse_infill_region_radius - tiny_overlap_radius, ClipperLib::jtSquare); narrow_sparse_infill_region_radius - tiny_overlap_radius, ClipperLib::jtSquare);
Polygons internal_volume;
{
Polygons shrinked_bottom_slice = idx_layer > 0 ? to_polygons(m_layers[idx_layer - 1]->lslices) : Polygons{};
Polygons shrinked_upper_slice = (idx_layer + 1) < m_layers.size() ?
to_polygons(m_layers[idx_layer + 1]->lslices) :
Polygons{};
internal_volume = intersection(shrinked_bottom_slice, shrinked_upper_slice);
}
// The opening operation may cause scattered tiny drops on the smooth parts of the model, filter them out // The opening operation may cause scattered tiny drops on the smooth parts of the model, filter them out
regularized_shell.erase(std::remove_if(regularized_shell.begin(), regularized_shell.end(), regularized_shell.erase(std::remove_if(regularized_shell.begin(), regularized_shell.end(),
[&min_perimeter_infill_spacing](const ExPolygon &p) { [&min_perimeter_infill_spacing, &internal_volume](const ExPolygon &p) {
return p.area() < min_perimeter_infill_spacing * scaled(8.0); return p.area() < min_perimeter_infill_spacing * scaled(1.5) ||
(p.area() < min_perimeter_infill_spacing * scaled(8.0) &&
diff(to_polygons(p), internal_volume).empty());
}), }),
regularized_shell.end()); regularized_shell.end());
} }
@ -1600,21 +1632,18 @@ void PrintObject::bridge_over_infill()
int layer_index, int layer_index,
Polygons new_polys, Polygons new_polys,
const LayerRegion *region, const LayerRegion *region,
double bridge_angle, double bridge_angle)
bool supported_by_lightning)
: original_surface(original_surface) : original_surface(original_surface)
, layer_index(layer_index) , layer_index(layer_index)
, new_polys(new_polys) , new_polys(new_polys)
, region(region) , region(region)
, bridge_angle(bridge_angle) , bridge_angle(bridge_angle)
, supported_by_lightning(supported_by_lightning)
{} {}
const Surface *original_surface; const Surface *original_surface;
int layer_index; int layer_index;
Polygons new_polys; Polygons new_polys;
const LayerRegion *region; const LayerRegion *region;
double bridge_angle; double bridge_angle;
bool supported_by_lightning;
}; };
std::map<size_t, std::vector<CandidateSurface>> surfaces_by_layer; std::map<size_t, std::vector<CandidateSurface>> surfaces_by_layer;
@ -1624,6 +1653,7 @@ void PrintObject::bridge_over_infill()
tbb::concurrent_vector<CandidateSurface> candidate_surfaces; tbb::concurrent_vector<CandidateSurface> candidate_surfaces;
tbb::parallel_for(tbb::blocked_range<size_t>(0, this->layers().size()), [po = static_cast<const PrintObject *>(this), tbb::parallel_for(tbb::blocked_range<size_t>(0, this->layers().size()), [po = static_cast<const PrintObject *>(this),
&candidate_surfaces](tbb::blocked_range<size_t> r) { &candidate_surfaces](tbb::blocked_range<size_t> r) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { for (size_t lidx = r.begin(); lidx < r.end(); lidx++) {
const Layer *layer = po->get_layer(lidx); const Layer *layer = po->get_layer(lidx);
if (layer->lower_layer == nullptr) { if (layer->lower_layer == nullptr) {
@ -1674,7 +1704,7 @@ void PrintObject::bridge_over_infill()
} }
} }
worth_bridging = intersection(closing(worth_bridging, SCALED_EPSILON), s->expolygon); worth_bridging = intersection(closing(worth_bridging, SCALED_EPSILON), s->expolygon);
candidate_surfaces.push_back(CandidateSurface(s, lidx, worth_bridging, region, 0, contains_only_lightning)); candidate_surfaces.push_back(CandidateSurface(s, lidx, worth_bridging, region, 0));
#ifdef DEBUG_BRIDGE_OVER_INFILL #ifdef DEBUG_BRIDGE_OVER_INFILL
debug_draw(std::to_string(lidx) + "_candidate_surface_" + std::to_string(area(s->expolygon)), debug_draw(std::to_string(lidx) + "_candidate_surface_" + std::to_string(area(s->expolygon)),
@ -1721,6 +1751,7 @@ void PrintObject::bridge_over_infill()
tbb::parallel_for(tbb::blocked_range<size_t>(0, layers_to_generate_infill.size()), [po = static_cast<const PrintObject *>(this), tbb::parallel_for(tbb::blocked_range<size_t>(0, layers_to_generate_infill.size()), [po = static_cast<const PrintObject *>(this),
&layers_to_generate_infill, &layers_to_generate_infill,
&infill_lines](tbb::blocked_range<size_t> r) { &infill_lines](tbb::blocked_range<size_t> r) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) { for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) {
size_t lidx = layers_to_generate_infill[job_idx]; size_t lidx = layers_to_generate_infill[job_idx];
infill_lines.at( infill_lines.at(
@ -1738,7 +1769,7 @@ void PrintObject::bridge_over_infill()
// cluster layers by depth needed for thick bridges. Each cluster is to be processed by single thread sequentially, so that bridges cannot appear one on another // cluster layers by depth needed for thick bridges. Each cluster is to be processed by single thread sequentially, so that bridges cannot appear one on another
std::vector<std::vector<size_t>> clustered_layers_for_threads; std::vector<std::vector<size_t>> clustered_layers_for_threads;
float target_flow_height_factor = 0.75; float target_flow_height_factor = 0.9;
{ {
std::vector<size_t> layers_with_candidates; std::vector<size_t> layers_with_candidates;
std::map<size_t, Polygons> layer_area_covered_by_candidates; std::map<size_t, Polygons> layer_area_covered_by_candidates;
@ -1752,6 +1783,7 @@ void PrintObject::bridge_over_infill()
tbb::parallel_for(tbb::blocked_range<size_t>(0, layers_with_candidates.size()), [&layers_with_candidates, &surfaces_by_layer, tbb::parallel_for(tbb::blocked_range<size_t>(0, layers_with_candidates.size()), [&layers_with_candidates, &surfaces_by_layer,
&layer_area_covered_by_candidates]( &layer_area_covered_by_candidates](
tbb::blocked_range<size_t> r) { tbb::blocked_range<size_t> r) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) { for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) {
size_t lidx = layers_with_candidates[job_idx]; size_t lidx = layers_with_candidates[job_idx];
for (const auto &candidate : surfaces_by_layer.at(lidx)) { for (const auto &candidate : surfaces_by_layer.at(lidx)) {
@ -1767,7 +1799,7 @@ void PrintObject::bridge_over_infill()
if (clustered_layers_for_threads.empty() || if (clustered_layers_for_threads.empty() ||
this->get_layer(clustered_layers_for_threads.back().back())->print_z < this->get_layer(clustered_layers_for_threads.back().back())->print_z <
this->get_layer(pair.first)->print_z - this->get_layer(pair.first)->print_z -
this->get_layer(pair.first)->regions()[0]->flow(frSolidInfill, true).height() * target_flow_height_factor - this->get_layer(pair.first)->regions()[0]->bridging_flow(frSolidInfill, true).height() * target_flow_height_factor -
EPSILON || EPSILON ||
intersection(layer_area_covered_by_candidates[clustered_layers_for_threads.back().back()], intersection(layer_area_covered_by_candidates[clustered_layers_for_threads.back().back()],
layer_area_covered_by_candidates[pair.first]) layer_area_covered_by_candidates[pair.first])
@ -1797,9 +1829,9 @@ void PrintObject::bridge_over_infill()
ExPolygons not_sparse_infill{}; ExPolygons not_sparse_infill{};
double bottom_z = po->get_layer(lidx)->print_z - target_flow_height * target_flow_height_factor - EPSILON; double bottom_z = po->get_layer(lidx)->print_z - target_flow_height * target_flow_height_factor - EPSILON;
for (int i = int(lidx) - 1; i >= 0; --i) { for (int i = int(lidx) - 1; i >= 0; --i) {
// Stop iterating if layer is lower than bottom_z. // Stop iterating if layer is lower than bottom_z and at least one iteration was made
const Layer *layer = po->get_layer(i); const Layer *layer = po->get_layer(i);
if (layer->print_z < bottom_z) if (layer->print_z < bottom_z && i < int(lidx) - 1)
break; break;
for (const LayerRegion *region : layer->regions()) { for (const LayerRegion *region : layer->regions()) {
@ -1990,8 +2022,8 @@ void PrintObject::bridge_over_infill()
// reconstruct polygon from polygon sections // reconstruct polygon from polygon sections
struct TracedPoly struct TracedPoly
{ {
std::vector<Point> lows; Points lows;
std::vector<Point> highs; Points highs;
}; };
std::vector<TracedPoly> current_traced_polys; std::vector<TracedPoly> current_traced_polys;
@ -2070,6 +2102,7 @@ void PrintObject::bridge_over_infill()
determine_bridging_angle, determine_bridging_angle,
construct_anchored_polygon]( construct_anchored_polygon](
tbb::blocked_range<size_t> r) { tbb::blocked_range<size_t> r) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
for (size_t cluster_idx = r.begin(); cluster_idx < r.end(); cluster_idx++) { for (size_t cluster_idx = r.begin(); cluster_idx < r.end(); cluster_idx++) {
for (size_t job_idx = 0; job_idx < clustered_layers_for_threads[cluster_idx].size(); job_idx++) { for (size_t job_idx = 0; job_idx < clustered_layers_for_threads[cluster_idx].size(); job_idx++) {
size_t lidx = clustered_layers_for_threads[cluster_idx][job_idx]; size_t lidx = clustered_layers_for_threads[cluster_idx][job_idx];
@ -2102,9 +2135,10 @@ void PrintObject::bridge_over_infill()
} }
// Gather deep infill areas, where thick bridges fit // Gather deep infill areas, where thick bridges fit
coordf_t spacing = surfaces_by_layer[lidx].front().region->flow(frSolidInfill, true).scaled_spacing(); coordf_t spacing = surfaces_by_layer[lidx].front().region->bridging_flow(frSolidInfill, true).scaled_spacing();
coordf_t target_flow_height = surfaces_by_layer[lidx].front().region->flow(frSolidInfill, true).height() * target_flow_height_factor; coordf_t target_flow_height = surfaces_by_layer[lidx].front().region->bridging_flow(frSolidInfill, true).height() *
Polygons deep_infill_area = gather_areas_w_depth(po, lidx, target_flow_height); target_flow_height_factor;
Polygons deep_infill_area = gather_areas_w_depth(po, lidx, target_flow_height);
{ {
// Now also remove area that has been already filled on lower layers by bridging expansion - For this // Now also remove area that has been already filled on lower layers by bridging expansion - For this
@ -2131,6 +2165,7 @@ void PrintObject::bridge_over_infill()
deep_infill_area = expand(deep_infill_area, spacing * 1.5); deep_infill_area = expand(deep_infill_area, spacing * 1.5);
// Now gather expansion polygons - internal infill on current layer, from which we can cut off anchors // Now gather expansion polygons - internal infill on current layer, from which we can cut off anchors
Polygons lightning_area;
Polygons expansion_area; Polygons expansion_area;
Polygons total_fill_area; Polygons total_fill_area;
for (const LayerRegion *region : layer->regions()) { for (const LayerRegion *region : layer->regions()) {
@ -2138,25 +2173,36 @@ void PrintObject::bridge_over_infill()
expansion_area.insert(expansion_area.end(), internal_polys.begin(), internal_polys.end()); expansion_area.insert(expansion_area.end(), internal_polys.begin(), internal_polys.end());
Polygons fill_polys = to_polygons(region->fill_expolygons()); Polygons fill_polys = to_polygons(region->fill_expolygons());
total_fill_area.insert(total_fill_area.end(), fill_polys.begin(), fill_polys.end()); total_fill_area.insert(total_fill_area.end(), fill_polys.begin(), fill_polys.end());
if (region->region().config().fill_pattern == ipLightning) {
Polygons l = to_polygons(region->fill_surfaces().filter_by_type(stInternal));
lightning_area.insert(lightning_area.end(), l.begin(), l.end());
}
} }
total_fill_area = closing(total_fill_area, SCALED_EPSILON); total_fill_area = closing(total_fill_area, SCALED_EPSILON);
expansion_area = closing(expansion_area, SCALED_EPSILON); expansion_area = closing(expansion_area, SCALED_EPSILON);
expansion_area = intersection(expansion_area, deep_infill_area); expansion_area = intersection(expansion_area, deep_infill_area);
Polylines anchors = intersection_pl(infill_lines[lidx - 1], shrink(expansion_area, spacing)); Polylines anchors = intersection_pl(infill_lines[lidx - 1], shrink(expansion_area, spacing));
Polygons internal_unsupported_area = shrink(deep_infill_area, spacing * 4.5);
#ifdef DEBUG_BRIDGE_OVER_INFILL #ifdef DEBUG_BRIDGE_OVER_INFILL
debug_draw(std::to_string(lidx) + "_" + std::to_string(cluster_idx) + "_" + std::to_string(job_idx) + "_" + "_total_area", debug_draw(std::to_string(lidx) + "_" + std::to_string(cluster_idx) + "_" + std::to_string(job_idx) + "_" + "_total_area",
to_lines(total_fill_area), to_lines(expansion_area), to_lines(deep_infill_area), to_lines(anchors)); to_lines(total_fill_area), to_lines(expansion_area), to_lines(deep_infill_area), to_lines(anchors));
#endif #endif
std::vector<CandidateSurface> expanded_surfaces; std::vector<CandidateSurface> expanded_surfaces;
expanded_surfaces.reserve(surfaces_by_layer[lidx].size()); expanded_surfaces.reserve(surfaces_by_layer[lidx].size());
for (const CandidateSurface &candidate : surfaces_by_layer[lidx]) { for (const CandidateSurface &candidate : surfaces_by_layer[lidx]) {
const Flow &flow = candidate.region->bridging_flow(frSolidInfill, true); const Flow &flow = candidate.region->bridging_flow(frSolidInfill, true);
Polygons area_to_be_bridge = expand(candidate.new_polys, flow.scaled_spacing()); Polygons area_to_be_bridge = expand(candidate.new_polys, flow.scaled_spacing());
area_to_be_bridge = intersection(area_to_be_bridge, deep_infill_area); area_to_be_bridge = intersection(area_to_be_bridge, deep_infill_area);
Polygons limiting_area = union_(area_to_be_bridge, expansion_area);
area_to_be_bridge.erase(std::remove_if(area_to_be_bridge.begin(), area_to_be_bridge.end(),
[internal_unsupported_area](const Polygon &p) {
return intersection({p}, internal_unsupported_area).empty();
}),
area_to_be_bridge.end());
Polygons limiting_area = union_(area_to_be_bridge, expansion_area);
if (area_to_be_bridge.empty()) if (area_to_be_bridge.empty())
continue; continue;
@ -2185,7 +2231,7 @@ void PrintObject::bridge_over_infill()
} }
boundary_plines.insert(boundary_plines.end(), anchors.begin(), anchors.end()); boundary_plines.insert(boundary_plines.end(), anchors.begin(), anchors.end());
if (candidate.supported_by_lightning) { if (!lightning_area.empty() && !intersection(area_to_be_bridge, lightning_area).empty()) {
boundary_plines = intersection_pl(boundary_plines, expand(area_to_be_bridge, scale_(10))); boundary_plines = intersection_pl(boundary_plines, expand(area_to_be_bridge, scale_(10)));
} }
Polygons bridging_area = construct_anchored_polygon(area_to_be_bridge, to_lines(boundary_plines), flow, bridging_angle); Polygons bridging_area = construct_anchored_polygon(area_to_be_bridge, to_lines(boundary_plines), flow, bridging_angle);
@ -2218,7 +2264,7 @@ void PrintObject::bridge_over_infill()
#endif #endif
expanded_surfaces.push_back(CandidateSurface(candidate.original_surface, candidate.layer_index, bridging_area, expanded_surfaces.push_back(CandidateSurface(candidate.original_surface, candidate.layer_index, bridging_area,
candidate.region, bridging_angle, candidate.supported_by_lightning)); candidate.region, bridging_angle));
} }
surfaces_by_layer[lidx].swap(expanded_surfaces); surfaces_by_layer[lidx].swap(expanded_surfaces);
expanded_surfaces.clear(); expanded_surfaces.clear();
@ -2229,6 +2275,7 @@ void PrintObject::bridge_over_infill()
BOOST_LOG_TRIVIAL(info) << "Bridge over infill - Directions and expanded surfaces computed" << log_memory_info(); BOOST_LOG_TRIVIAL(info) << "Bridge over infill - Directions and expanded surfaces computed" << log_memory_info();
tbb::parallel_for(tbb::blocked_range<size_t>(0, this->layers().size()), [po = this, &surfaces_by_layer](tbb::blocked_range<size_t> r) { tbb::parallel_for(tbb::blocked_range<size_t>(0, this->layers().size()), [po = this, &surfaces_by_layer](tbb::blocked_range<size_t> r) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { for (size_t lidx = r.begin(); lidx < r.end(); lidx++) {
if (surfaces_by_layer.find(lidx) == surfaces_by_layer.end()) if (surfaces_by_layer.find(lidx) == surfaces_by_layer.end())
continue; continue;
@ -2745,6 +2792,7 @@ static void project_triangles_to_slabs(SpanOfConstPtrs<Layer> layers, const inde
[&custom_facets, &tr, tr_det_sign, seam, layers, &projections_of_triangles](const tbb::blocked_range<size_t>& range) { [&custom_facets, &tr, tr_det_sign, seam, layers, &projections_of_triangles](const tbb::blocked_range<size_t>& range) {
for (size_t idx = range.begin(); idx < range.end(); ++ idx) { for (size_t idx = range.begin(); idx < range.end(); ++ idx) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
std::array<Vec3f, 3> facet; std::array<Vec3f, 3> facet;
// Transform the triangle into worlds coords. // Transform the triangle into worlds coords.

View File

@ -43,7 +43,8 @@ Point ConcaveHull::centroid(const Points &pp)
Points ConcaveHull::calculate_centroids() const Points ConcaveHull::calculate_centroids() const
{ {
// We get the centroids of all the islands in the 2D slice // We get the centroids of all the islands in the 2D slice
Points centroids = reserve_vector<Point>(m_polys.size()); Points centroids;
centroids.reserve(m_polys.size());
std::transform(m_polys.begin(), m_polys.end(), std::transform(m_polys.begin(), m_polys.end(),
std::back_inserter(centroids), std::back_inserter(centroids),
[](const Polygon &poly) { return centroid(poly); }); [](const Polygon &poly) { return centroid(poly); });

View File

@ -6,6 +6,8 @@
#include <cmath> #include <cmath>
#include <string> #include <string>
#include <libslic3r/Point.hpp>
struct indexed_triangle_set; struct indexed_triangle_set;
namespace Slic3r { namespace Slic3r {
@ -13,7 +15,7 @@ namespace Slic3r {
class ExPolygon; class ExPolygon;
class Polygon; class Polygon;
using ExPolygons = std::vector<ExPolygon>; using ExPolygons = std::vector<ExPolygon>;
using Polygons = std::vector<Polygon>; using Polygons = std::vector<Polygon, PointsAllocator<Polygon>>;
namespace sla { namespace sla {

View File

@ -4,6 +4,7 @@
#include <vector> #include <vector>
#include <memory> #include <memory>
#include <libslic3r/Polygon.hpp>
#include <libslic3r/ExPolygon.hpp> #include <libslic3r/ExPolygon.hpp>
#include <libslic3r/AABBMesh.hpp> #include <libslic3r/AABBMesh.hpp>
@ -14,9 +15,6 @@
namespace Slic3r { namespace Slic3r {
using Polygons = std::vector<Polygon>;
using ExPolygons = std::vector<ExPolygon>;
namespace sla { namespace sla {
struct SupportTreeConfig struct SupportTreeConfig

View File

@ -6,6 +6,8 @@
#include <random> #include <random>
#include <algorithm> #include <algorithm>
#include <ankerl/unordered_dense.h>
namespace Slic3r { namespace Slic3r {
void its_short_edge_collpase(indexed_triangle_set &mesh, size_t target_triangle_count) { void its_short_edge_collpase(indexed_triangle_set &mesh, size_t target_triangle_count) {
@ -155,7 +157,7 @@ void its_short_edge_collpase(indexed_triangle_set &mesh, size_t target_triangle_
} }
//Extract the result mesh //Extract the result mesh
std::unordered_map<size_t, size_t> final_vertices_mapping; ankerl::unordered_dense::map<size_t, size_t> final_vertices_mapping;
std::vector<Vec3f> final_vertices; std::vector<Vec3f> final_vertices;
std::vector<Vec3i> final_indices; std::vector<Vec3i> final_indices;
final_indices.reserve(face_indices.size()); final_indices.reserve(face_indices.size());

View File

@ -8,10 +8,13 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
namespace ClipperLib { class PolyNode; }
namespace Slic3r { namespace Slic3r {
namespace ClipperLib {
class PolyNode;
using PolyNodes = std::vector<PolyNode*, PointsAllocator<PolyNode*>>;
}
class ExPolygon; class ExPolygon;
using ExPolygons = std::vector<ExPolygon>; using ExPolygons = std::vector<ExPolygon>;
@ -29,7 +32,7 @@ void chain_and_reorder_extrusion_paths(std::vect
Polylines chain_polylines(Polylines &&src, const Point *start_near = nullptr); Polylines chain_polylines(Polylines &&src, const Point *start_near = nullptr);
inline Polylines chain_polylines(const Polylines& src, const Point* start_near = nullptr) { Polylines tmp(src); return chain_polylines(std::move(tmp), start_near); } inline Polylines chain_polylines(const Polylines& src, const Point* start_near = nullptr) { Polylines tmp(src); return chain_polylines(std::move(tmp), start_near); }
std::vector<ClipperLib::PolyNode*> chain_clipper_polynodes(const Points &points, const std::vector<ClipperLib::PolyNode*> &items); ClipperLib::PolyNodes chain_clipper_polynodes(const Points &points, const ClipperLib::PolyNodes &items);
// Chain instances of print objects by an approximate shortest path. // Chain instances of print objects by an approximate shortest path.
// Returns pairs of PrintObject idx and instance of that PrintObject. // Returns pairs of PrintObject idx and instance of that PrintObject.

View File

@ -13,6 +13,7 @@
#include "PrintBase.hpp" #include "PrintBase.hpp"
#include "PrintConfig.hpp" #include "PrintConfig.hpp"
#include "Tesselate.hpp" #include "Tesselate.hpp"
#include "Utils.hpp"
#include "libslic3r.h" #include "libslic3r.h"
#include "tbb/parallel_for.h" #include "tbb/parallel_for.h"
#include "tbb/blocked_range.h" #include "tbb/blocked_range.h"
@ -24,6 +25,8 @@
#include <cstddef> #include <cstddef>
#include <cstdio> #include <cstdio>
#include <functional> #include <functional>
#include <limits>
#include <math.h>
#include <optional> #include <optional>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
@ -206,18 +209,38 @@ std::vector<ExtrusionLine> to_short_lines(const ExtrusionEntity *e, float length
} }
float estimate_curled_up_height( float estimate_curled_up_height(
const ExtendedPoint &point, float layer_height, float flow_width, float prev_line_curled_height, Params params) float distance, float curvature, float layer_height, float flow_width, float prev_line_curled_height, Params params)
{ {
float curled_up_height = 0.0f; float curled_up_height = 0;
if (fabs(point.distance) < 1.5 * flow_width) { if (fabs(distance) < 3.0 * flow_width) {
curled_up_height = 0.85 * prev_line_curled_height; curled_up_height = std::max(prev_line_curled_height - layer_height * 0.75f, 0.0f);
} }
if (point.distance > params.malformation_distance_factors.first * flow_width &&
point.distance < params.malformation_distance_factors.second * flow_width && point.curvature > -0.1f) {
float dist_factor = std::max(point.distance - params.malformation_distance_factors.first * flow_width, 0.01f) /
((params.malformation_distance_factors.second - params.malformation_distance_factors.first) * flow_width);
curled_up_height = layer_height * sqrt(sqrt(dist_factor)) * std::clamp(3.0f * point.curvature, 1.0f, 3.0f); if (distance > params.malformation_distance_factors.first * flow_width &&
distance < params.malformation_distance_factors.second * flow_width) {
// imagine the extrusion profile. The part that has been glued (melted) with the previous layer will be called anchored section
// and the rest will be called curling section
// float anchored_section = flow_width - point.distance;
float curling_section = distance;
// after extruding, the curling (floating) part of the extrusion starts to shrink back to the rounded shape of the nozzle
// The anchored part not, because the melted material holds to the previous layer well.
// We can assume for simplicity perfect equalization of layer height and raising part width, from which:
float swelling_radius = (layer_height + curling_section) / 2.0f;
curled_up_height += std::max(0.f, (swelling_radius - layer_height) / 2.0f);
// On convex turns, there is larger tension on the floating edge of the extrusion then on the middle section.
// The tension is caused by the shrinking tendency of the filament, and on outer edge of convex trun, the expansion is greater and
// thus shrinking force is greater. This tension will cause the curling section to curle up
if (curvature > 0.01) {
float radius = (1.0 / curvature);
float curling_t = sqrt(radius / 100);
float b = curling_t * flow_width;
float a = curling_section;
float c = sqrt(std::max(0.0f, a * a - b * b));
curled_up_height += c;
}
curled_up_height = std::min(curled_up_height, params.max_curled_height_factor * layer_height); curled_up_height = std::min(curled_up_height, params.max_curled_height_factor * layer_height);
} }
@ -312,9 +335,9 @@ std::vector<ExtrusionLine> check_extrusion_entity_stability(const ExtrusionEntit
float line_len = (prev_point.position - curr_point.position).norm(); float line_len = (prev_point.position - curr_point.position).norm();
ExtrusionLine line_out{prev_point.position.cast<float>(), curr_point.position.cast<float>(), line_len, entity}; ExtrusionLine line_out{prev_point.position.cast<float>(), curr_point.position.cast<float>(), line_len, entity};
const ExtrusionLine nearest_prev_layer_line = prev_layer_lines.get_lines().size() > 0 ? Vec2f middle = 0.5 * (line_out.a + line_out.b);
prev_layer_lines.get_line(curr_point.nearest_prev_layer_line) : auto [middle_distance, bottom_line_idx, x] = prev_layer_lines.distance_from_lines_extra<false>(middle);
ExtrusionLine{}; ExtrusionLine bottom_line = prev_layer_lines.get_lines().empty() ? ExtrusionLine{} : prev_layer_lines.get_line(bottom_line_idx);
// correctify the distance sign using slice polygons // correctify the distance sign using slice polygons
float sign = (prev_layer_boundary.distance_from_lines<true>(curr_point.position) + 0.5f * flow_width) < 0.0f ? -1.0f : 1.0f; float sign = (prev_layer_boundary.distance_from_lines<true>(curr_point.position) + 0.5f * flow_width) < 0.0f ? -1.0f : 1.0f;
@ -339,7 +362,7 @@ std::vector<ExtrusionLine> check_extrusion_entity_stability(const ExtrusionEntit
} }
} else if (curr_point.distance > flow_width * 0.8f) { } else if (curr_point.distance > flow_width * 0.8f) {
bridged_distance += line_len; bridged_distance += line_len;
line_out.form_quality = nearest_prev_layer_line.form_quality - 0.3f; line_out.form_quality = bottom_line.form_quality - 0.3f;
if (line_out.form_quality < 0 && bridged_distance > max_bridge_len) { if (line_out.form_quality < 0 && bridged_distance > max_bridge_len) {
line_out.support_point_generated = potential_cause; line_out.support_point_generated = potential_cause;
line_out.form_quality = 0.5f; line_out.form_quality = 0.5f;
@ -349,8 +372,9 @@ std::vector<ExtrusionLine> check_extrusion_entity_stability(const ExtrusionEntit
bridged_distance = 0.0f; bridged_distance = 0.0f;
} }
line_out.curled_up_height = estimate_curled_up_height(curr_point, layer_region->layer()->height, flow_width, line_out.curled_up_height = estimate_curled_up_height(middle_distance, 0.5 * (prev_point.curvature + curr_point.curvature),
nearest_prev_layer_line.curled_up_height, params); layer_region->layer()->height, flow_width, bottom_line.curled_up_height,
params);
lines_out.push_back(line_out); lines_out.push_back(line_out);
} }
@ -1043,7 +1067,7 @@ void estimate_supports_malformations(SupportLayerPtrs &layers, float flow_width,
AABBTreeLines::LinesDistancer<ExtrusionLine> prev_layer_lines{}; AABBTreeLines::LinesDistancer<ExtrusionLine> prev_layer_lines{};
for (SupportLayer *l : layers) { for (SupportLayer *l : layers) {
l->malformed_lines.clear(); l->curled_lines.clear();
std::vector<ExtrusionLine> current_layer_lines; std::vector<ExtrusionLine> current_layer_lines;
for (const ExtrusionEntity *extrusion : l->support_fills.flatten().entities) { for (const ExtrusionEntity *extrusion : l->support_fills.flatten().entities) {
@ -1054,24 +1078,23 @@ void estimate_supports_malformations(SupportLayerPtrs &layers, float flow_width,
auto annotated_points = estimate_points_properties<true, true, false, false>(pol.points, prev_layer_lines, flow_width); auto annotated_points = estimate_points_properties<true, true, false, false>(pol.points, prev_layer_lines, flow_width);
for (size_t i = 0; i < annotated_points.size(); ++i) { for (size_t i = 0; i < annotated_points.size(); ++i) {
ExtendedPoint &curr_point = annotated_points[i]; const ExtendedPoint &a = i > 0 ? annotated_points[i - 1] : annotated_points[i];
float line_len = i > 0 ? ((annotated_points[i - 1].position - curr_point.position).norm()) : 0.0f; const ExtendedPoint &b = annotated_points[i];
ExtrusionLine line_out{i > 0 ? annotated_points[i - 1].position.cast<float>() : curr_point.position.cast<float>(), ExtrusionLine line_out{a.position.cast<float>(), b.position.cast<float>(), float((a.position - b.position).norm()),
curr_point.position.cast<float>(), line_len, extrusion}; extrusion};
const ExtrusionLine nearest_prev_layer_line = prev_layer_lines.get_lines().size() > 0 ? Vec2f middle = 0.5 * (line_out.a + line_out.b);
prev_layer_lines.get_line(curr_point.nearest_prev_layer_line) : auto [middle_distance, bottom_line_idx, x] = prev_layer_lines.distance_from_lines_extra<false>(middle);
ExtrusionLine{}; ExtrusionLine bottom_line = prev_layer_lines.get_lines().empty() ? ExtrusionLine{} :
prev_layer_lines.get_line(bottom_line_idx);
Vec2f v1 = (nearest_prev_layer_line.b - nearest_prev_layer_line.a); Vec2f v1 = (bottom_line.b - bottom_line.a);
Vec2f v2 = (curr_point.position.cast<float>() - nearest_prev_layer_line.a); Vec2f v2 = (a.position.cast<float>() - bottom_line.a);
auto d = (v1.x() * v2.y()) - (v1.y() * v2.x()); auto d = (v1.x() * v2.y()) - (v1.y() * v2.x());
if (d > 0) { float sign = (d > 0) ? -1.0f : 1.0f;
curr_point.distance *= -1.0f;
}
line_out.curled_up_height = estimate_curled_up_height(curr_point, l->height, flow_width, line_out.curled_up_height = estimate_curled_up_height(middle_distance * sign, 0.5 * (a.curvature + b.curvature), l->height,
nearest_prev_layer_line.curled_up_height, params); flow_width, bottom_line.curled_up_height, params);
current_layer_lines.push_back(line_out); current_layer_lines.push_back(line_out);
} }
@ -1079,7 +1102,7 @@ void estimate_supports_malformations(SupportLayerPtrs &layers, float flow_width,
for (const ExtrusionLine &line : current_layer_lines) { for (const ExtrusionLine &line : current_layer_lines) {
if (line.curled_up_height > params.curling_tolerance_limit) { if (line.curled_up_height > params.curling_tolerance_limit) {
l->malformed_lines.push_back(Line{Point::new_scale(line.a), Point::new_scale(line.b)}); l->curled_lines.push_back(CurledLine{Point::new_scale(line.a), Point::new_scale(line.b), line.curled_up_height});
} }
} }
@ -1109,42 +1132,42 @@ void estimate_malformations(LayerPtrs &layers, const Params &params)
{ {
#ifdef DEBUG_FILES #ifdef DEBUG_FILES
FILE *debug_file = boost::nowide::fopen(debug_out_path("object_malformations.obj").c_str(), "w"); FILE *debug_file = boost::nowide::fopen(debug_out_path("object_malformations.obj").c_str(), "w");
FILE *full_file = boost::nowide::fopen(debug_out_path("object_full.obj").c_str(), "w"); FILE *full_file = boost::nowide::fopen(debug_out_path("object_full.obj").c_str(), "w");
#endif #endif
LD prev_layer_lines{}; LD prev_layer_lines{};
for (Layer *l : layers) { for (Layer *l : layers) {
l->malformed_lines.clear(); l->curled_lines.clear();
std::vector<Linef> boundary_lines = l->lower_layer != nullptr ? to_unscaled_linesf(l->lower_layer->lslices) : std::vector<Linef>(); std::vector<Linef> boundary_lines = l->lower_layer != nullptr ? to_unscaled_linesf(l->lower_layer->lslices) : std::vector<Linef>();
AABBTreeLines::LinesDistancer<Linef> prev_layer_boundary{std::move(boundary_lines)}; AABBTreeLines::LinesDistancer<Linef> prev_layer_boundary{std::move(boundary_lines)};
std::vector<ExtrusionLine> current_layer_lines; std::vector<ExtrusionLine> current_layer_lines;
for (const LayerRegion *layer_region : l->regions()) { for (const LayerRegion *layer_region : l->regions()) {
for (const ExtrusionEntity *extrusion : layer_region->perimeters().flatten().entities) { for (const ExtrusionEntity *extrusion : layer_region->perimeters().flatten().entities) {
if (!extrusion->role().is_external_perimeter())
if (!extrusion->role().is_external_perimeter()) continue; continue;
Points extrusion_pts; Points extrusion_pts;
extrusion->collect_points(extrusion_pts); extrusion->collect_points(extrusion_pts);
float flow_width = get_flow_width(layer_region, extrusion->role()); float flow_width = get_flow_width(layer_region, extrusion->role());
auto annotated_points = estimate_points_properties<true, false, false, false>(extrusion_pts, prev_layer_lines, flow_width, auto annotated_points = estimate_points_properties<true, true, false, false>(extrusion_pts, prev_layer_lines, flow_width,
params.bridge_distance); params.bridge_distance);
for (size_t i = 0; i < annotated_points.size(); ++i) { for (size_t i = 0; i < annotated_points.size(); ++i) {
ExtendedPoint &curr_point = annotated_points[i]; const ExtendedPoint &a = i > 0 ? annotated_points[i - 1] : annotated_points[i];
float line_len = i > 0 ? ((annotated_points[i - 1].position - curr_point.position).norm()) : 0.0f; const ExtendedPoint &b = annotated_points[i];
ExtrusionLine line_out{i > 0 ? annotated_points[i - 1].position.cast<float>() : curr_point.position.cast<float>(), ExtrusionLine line_out{a.position.cast<float>(), b.position.cast<float>(), float((a.position - b.position).norm()),
curr_point.position.cast<float>(), line_len, extrusion}; extrusion};
const ExtrusionLine nearest_prev_layer_line = prev_layer_lines.get_lines().size() > 0 ? Vec2f middle = 0.5 * (line_out.a + line_out.b);
prev_layer_lines.get_line(curr_point.nearest_prev_layer_line) : auto [middle_distance, bottom_line_idx, x] = prev_layer_lines.distance_from_lines_extra<false>(middle);
ExtrusionLine{}; ExtrusionLine bottom_line = prev_layer_lines.get_lines().empty() ? ExtrusionLine{} :
prev_layer_lines.get_line(bottom_line_idx);
float sign = (prev_layer_boundary.distance_from_lines<true>(curr_point.position) + 0.5f * flow_width) < 0.0f ? -1.0f : // correctify the distance sign using slice polygons
1.0f; float sign = (prev_layer_boundary.distance_from_lines<true>(middle.cast<double>()) + 0.5f * flow_width) < 0.0f ? -1.0f : 1.0f;
curr_point.distance *= sign;
line_out.curled_up_height = estimate_curled_up_height(curr_point, layer_region->layer()->height, flow_width, line_out.curled_up_height = estimate_curled_up_height(middle_distance * sign, 0.5 * (a.curvature + b.curvature),
nearest_prev_layer_line.curled_up_height, params); l->height, flow_width, bottom_line.curled_up_height, params);
current_layer_lines.push_back(line_out); current_layer_lines.push_back(line_out);
} }
@ -1153,7 +1176,7 @@ void estimate_malformations(LayerPtrs &layers, const Params &params)
for (const ExtrusionLine &line : current_layer_lines) { for (const ExtrusionLine &line : current_layer_lines) {
if (line.curled_up_height > params.curling_tolerance_limit) { if (line.curled_up_height > params.curling_tolerance_limit) {
l->malformed_lines.push_back(Line{Point::new_scale(line.a), Point::new_scale(line.b)}); l->curled_lines.push_back(CurledLine{Point::new_scale(line.a), Point::new_scale(line.b), line.curled_up_height});
} }
} }
@ -1164,9 +1187,9 @@ void estimate_malformations(LayerPtrs &layers, const Params &params)
fprintf(debug_file, "v %f %f %f %f %f %f\n", line.b[0], line.b[1], l->print_z, color[0], color[1], color[2]); fprintf(debug_file, "v %f %f %f %f %f %f\n", line.b[0], line.b[1], l->print_z, color[0], color[1], color[2]);
} }
} }
for (const ExtrusionLine &line : current_layer_lines) { for (const ExtrusionLine &line : current_layer_lines) {
Vec3f color = value_to_rgbf(-EPSILON, l->height * params.max_curled_height_factor, line.curled_up_height); Vec3f color = value_to_rgbf(-EPSILON, l->height * params.max_curled_height_factor, line.curled_up_height);
fprintf(full_file, "v %f %f %f %f %f %f\n", line.b[0], line.b[1], l->print_z, color[0], color[1], color[2]); fprintf(full_file, "v %f %f %f %f %f %f\n", line.b[0], line.b[1], l->print_z, color[0], color[1], color[2]);
} }
#endif #endif

View File

@ -42,7 +42,7 @@ struct Params
BrimType brim_type; BrimType brim_type;
const float brim_width; const float brim_width;
const std::pair<float,float> malformation_distance_factors = std::pair<float, float> { 0.5, 1.1 }; const std::pair<float,float> malformation_distance_factors = std::pair<float, float> { 0.2, 1.1 };
const float max_curled_height_factor = 10.0f; const float max_curled_height_factor = 10.0f;
const float curling_tolerance_limit = 0.1f; const float curling_tolerance_limit = 0.1f;

View File

@ -58,8 +58,6 @@
// Enable alternative version of file_wildcards() // Enable alternative version of file_wildcards()
#define ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR (1 && ENABLE_2_6_0_ALPHA1) #define ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR (1 && ENABLE_2_6_0_ALPHA1)
// Enable gcode postprocess modified to allow for backward insertion of new lines
#define ENABLE_GCODE_POSTPROCESS_BACKTRACE (1 && ENABLE_2_6_0_ALPHA1)
#endif // _prusaslicer_technologies_h_ #endif // _prusaslicer_technologies_h_

View File

@ -10,3 +10,12 @@ Slic3r::Timer::~Timer()
BOOST_LOG_TRIVIAL(debug) << "Timer '" << m_name << "' spend " << BOOST_LOG_TRIVIAL(debug) << "Timer '" << m_name << "' spend " <<
duration_cast<milliseconds>(steady_clock::now() - m_start).count() << "ms"; duration_cast<milliseconds>(steady_clock::now() - m_start).count() << "ms";
} }
namespace Slic3r::Timing {
void TimeLimitAlarm::report_time_exceeded() const {
BOOST_LOG_TRIVIAL(error) << "Time limit exceeded for " << m_limit_exceeded_message << ": " << m_timer.elapsed_seconds() << "s";
}
}

View File

@ -27,5 +27,66 @@ public:
~Timer(); ~Timer();
}; };
namespace Timing {
// Timing code from Catch2 unit testing library
static inline uint64_t nanoseconds_since_epoch() {
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
}
// Timing code from Catch2 unit testing library
class Timer {
public:
void start() {
m_nanoseconds = nanoseconds_since_epoch();
}
uint64_t elapsed_nanoseconds() const {
return nanoseconds_since_epoch() - m_nanoseconds;
}
uint64_t elapsed_microseconds() const {
return elapsed_nanoseconds() / 1000;
}
unsigned int elapsed_milliseconds() const {
return static_cast<unsigned int>(elapsed_microseconds()/1000);
}
double elapsed_seconds() const {
return elapsed_microseconds() / 1000000.0;
}
private:
uint64_t m_nanoseconds = 0;
};
// Emits a Boost::log error if the life time of this timing object exceeds a limit.
class TimeLimitAlarm {
public:
TimeLimitAlarm(uint64_t time_limit_nanoseconds, std::string_view limit_exceeded_message) :
m_time_limit_nanoseconds(time_limit_nanoseconds), m_limit_exceeded_message(limit_exceeded_message) {
m_timer.start();
}
~TimeLimitAlarm() {
auto elapsed = m_timer.elapsed_nanoseconds();
if (elapsed > m_time_limit_nanoseconds)
this->report_time_exceeded();
}
static TimeLimitAlarm new_nanos(uint64_t time_limit_nanoseconds, std::string_view limit_exceeded_message) {
return TimeLimitAlarm(time_limit_nanoseconds, limit_exceeded_message);
}
static TimeLimitAlarm new_milis(uint64_t time_limit_milis, std::string_view limit_exceeded_message) {
return TimeLimitAlarm(uint64_t(time_limit_milis) * 1000000l, limit_exceeded_message);
}
static TimeLimitAlarm new_seconds(uint64_t time_limit_seconds, std::string_view limit_exceeded_message) {
return TimeLimitAlarm(uint64_t(time_limit_seconds) * 1000000000l, limit_exceeded_message);
}
private:
void report_time_exceeded() const;
Timer m_timer;
uint64_t m_time_limit_nanoseconds;
std::string_view m_limit_exceeded_message;
};
} // namespace Catch
} // namespace Slic3r } // namespace Slic3r
#endif // libslic3r_Timer_hpp_ #endif // libslic3r_Timer_hpp_

View File

@ -257,6 +257,8 @@ void TreeModelVolumes::precalculate(const PrintObject& print_object, const coord
auto it = radius_until_layer.find(r); auto it = radius_until_layer.find(r);
if (it == radius_until_layer.end()) if (it == radius_until_layer.end())
radius_until_layer.emplace_hint(it, r, current_layer); radius_until_layer.emplace_hint(it, r, current_layer);
else
assert(it->second >= current_layer);
}; };
// regular radius // regular radius
update_radius_until_layer(ceilRadius(config.getRadius(distance_to_top, 0) + m_current_min_xy_dist_delta)); update_radius_until_layer(ceilRadius(config.getRadius(distance_to_top, 0) + m_current_min_xy_dist_delta));
@ -359,7 +361,7 @@ const Polygons& TreeModelVolumes::getCollision(const coord_t orig_radius, LayerI
BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate collision at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!"; BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate collision at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!";
tree_supports_show_error("Not precalculated Collision requested."sv, false); tree_supports_show_error("Not precalculated Collision requested."sv, false);
} }
const_cast<TreeModelVolumes*>(this)->calculateCollision(radius, layer_idx, {}); const_cast<TreeModelVolumes*>(this)->calculateCollision(radius, layer_idx, []{});
return getCollision(orig_radius, layer_idx, min_xy_dist); return getCollision(orig_radius, layer_idx, min_xy_dist);
} }

View File

@ -512,7 +512,7 @@ private:
*/ */
void calculateCollisionHolefree(RadiusLayerPair key) void calculateCollisionHolefree(RadiusLayerPair key)
{ {
calculateCollisionHolefree(std::vector<RadiusLayerPair>{ RadiusLayerPair(key) }, {}); calculateCollisionHolefree(std::vector<RadiusLayerPair>{ RadiusLayerPair(key) }, []{});
} }
/*! /*!
@ -533,7 +533,7 @@ private:
*/ */
void calculateAvoidance(RadiusLayerPair key, bool to_build_plate, bool to_model) void calculateAvoidance(RadiusLayerPair key, bool to_build_plate, bool to_model)
{ {
calculateAvoidance(std::vector<RadiusLayerPair>{ RadiusLayerPair(key) }, to_build_plate, to_model, {}); calculateAvoidance(std::vector<RadiusLayerPair>{ RadiusLayerPair(key) }, to_build_plate, to_model, []{});
} }
/*! /*!
@ -567,7 +567,7 @@ private:
*/ */
void calculateWallRestrictions(RadiusLayerPair key) void calculateWallRestrictions(RadiusLayerPair key)
{ {
calculateWallRestrictions(std::vector<RadiusLayerPair>{ RadiusLayerPair(key) }, {}); calculateWallRestrictions(std::vector<RadiusLayerPair>{ RadiusLayerPair(key) }, []{});
} }
/*! /*!

View File

@ -70,16 +70,17 @@ TreeSupportSettings::TreeSupportSettings(const TreeSupportMeshGroupSettings& mes
maximum_move_distance_slow((angle_slow < M_PI / 2.) ? (coord_t)(tan(angle_slow) * layer_height) : std::numeric_limits<coord_t>::max()), maximum_move_distance_slow((angle_slow < M_PI / 2.) ? (coord_t)(tan(angle_slow) * layer_height) : std::numeric_limits<coord_t>::max()),
support_bottom_layers(mesh_group_settings.support_bottom_enable ? (mesh_group_settings.support_bottom_height + layer_height / 2) / layer_height : 0), support_bottom_layers(mesh_group_settings.support_bottom_enable ? (mesh_group_settings.support_bottom_height + layer_height / 2) / layer_height : 0),
tip_layers(std::max((branch_radius - min_radius) / (support_line_width / 3), branch_radius / layer_height)), // Ensure lines always stack nicely even if layer height is large tip_layers(std::max((branch_radius - min_radius) / (support_line_width / 3), branch_radius / layer_height)), // Ensure lines always stack nicely even if layer height is large
diameter_angle_scale_factor(sin(mesh_group_settings.support_tree_branch_diameter_angle) * layer_height / branch_radius), branch_radius_increase_per_layer(tan(mesh_group_settings.support_tree_branch_diameter_angle) * layer_height),
max_to_model_radius_increase(mesh_group_settings.support_tree_max_diameter_increase_by_merges_when_support_to_model / 2), max_to_model_radius_increase(mesh_group_settings.support_tree_max_diameter_increase_by_merges_when_support_to_model / 2),
min_dtt_to_model(round_up_divide(mesh_group_settings.support_tree_min_height_to_model, layer_height)), min_dtt_to_model(round_up_divide(mesh_group_settings.support_tree_min_height_to_model, layer_height)),
increase_radius_until_radius(mesh_group_settings.support_tree_branch_diameter / 2), increase_radius_until_radius(mesh_group_settings.support_tree_branch_diameter / 2),
increase_radius_until_layer(increase_radius_until_radius <= branch_radius ? tip_layers * (increase_radius_until_radius / branch_radius) : (increase_radius_until_radius - branch_radius) / (branch_radius * diameter_angle_scale_factor)), increase_radius_until_layer(increase_radius_until_radius <= branch_radius ? tip_layers * (increase_radius_until_radius / branch_radius) : (increase_radius_until_radius - branch_radius) / branch_radius_increase_per_layer),
support_rests_on_model(! mesh_group_settings.support_material_buildplate_only), support_rests_on_model(! mesh_group_settings.support_material_buildplate_only),
xy_distance(mesh_group_settings.support_xy_distance), xy_distance(mesh_group_settings.support_xy_distance),
xy_min_distance(std::min(mesh_group_settings.support_xy_distance, mesh_group_settings.support_xy_distance_overhang)), xy_min_distance(std::min(mesh_group_settings.support_xy_distance, mesh_group_settings.support_xy_distance_overhang)),
bp_radius(mesh_group_settings.support_tree_bp_diameter / 2), bp_radius(mesh_group_settings.support_tree_bp_diameter / 2),
diameter_scale_bp_radius(std::min(sin(0.7) * layer_height / branch_radius, 1.0 / (branch_radius / (support_line_width / 2.0)))), // Either 40? or as much as possible so that 2 lines will overlap by at least 50%, whichever is smaller. // Increase by half a line overlap, but not faster than 40 degrees angle (0 degrees means zero increase in radius).
bp_radius_increase_per_layer(std::min(tan(0.7) * layer_height, 0.5 * support_line_width)),
z_distance_bottom_layers(size_t(round(double(mesh_group_settings.support_bottom_distance) / double(layer_height)))), z_distance_bottom_layers(size_t(round(double(mesh_group_settings.support_bottom_distance) / double(layer_height)))),
z_distance_top_layers(size_t(round(double(mesh_group_settings.support_top_distance) / double(layer_height)))), z_distance_top_layers(size_t(round(double(mesh_group_settings.support_top_distance) / double(layer_height)))),
performance_interface_skip_layers(round_up_divide(mesh_group_settings.support_interface_skip_height, layer_height)), performance_interface_skip_layers(round_up_divide(mesh_group_settings.support_interface_skip_height, layer_height)),
@ -96,7 +97,7 @@ TreeSupportSettings::TreeSupportSettings(const TreeSupportMeshGroupSettings& mes
settings(mesh_group_settings), settings(mesh_group_settings),
min_feature_size(mesh_group_settings.min_feature_size) min_feature_size(mesh_group_settings.min_feature_size)
{ {
layer_start_bp_radius = (bp_radius - branch_radius) / (branch_radius * diameter_scale_bp_radius); layer_start_bp_radius = (bp_radius - branch_radius) / bp_radius_increase_per_layer;
if (TreeSupportSettings::soluble) { if (TreeSupportSettings::soluble) {
// safeOffsetInc can only work in steps of the size xy_min_distance in the worst case => xy_min_distance has to be a bit larger than 0 in this worst case and should be large enough for performance to not suffer extremely // safeOffsetInc can only work in steps of the size xy_min_distance in the worst case => xy_min_distance has to be a bit larger than 0 in this worst case and should be large enough for performance to not suffer extremely
@ -1463,17 +1464,21 @@ static void generate_initial_areas(
const size_t num_support_roof_layers = mesh_group_settings.support_roof_enable ? (mesh_group_settings.support_roof_height + config.layer_height / 2) / config.layer_height : 0; const size_t num_support_roof_layers = mesh_group_settings.support_roof_enable ? (mesh_group_settings.support_roof_height + config.layer_height / 2) / config.layer_height : 0;
const bool roof_enabled = num_support_roof_layers > 0; const bool roof_enabled = num_support_roof_layers > 0;
const bool force_tip_to_roof = sqr<double>(config.min_radius) * M_PI > mesh_group_settings.minimum_roof_area && roof_enabled; const bool force_tip_to_roof = sqr<double>(config.min_radius) * M_PI > mesh_group_settings.minimum_roof_area && roof_enabled;
//FIXME mesh_group_settings.support_angle does not apply to enforcers and also it does not apply to automatic support angle (by half the external perimeter width).
//used by max_overhang_insert_lag, only if not min_xy_dist.
const coord_t max_overhang_speed = mesh_group_settings.support_angle < 0.5 * M_PI ? coord_t(tan(mesh_group_settings.support_angle) * config.layer_height) : std::numeric_limits<coord_t>::max();
// cap for how much layer below the overhang a new support point may be added, as other than with regular support every new inserted point // cap for how much layer below the overhang a new support point may be added, as other than with regular support every new inserted point
// may cause extra material and time cost. Could also be an user setting or differently calculated. Idea is that if an overhang // may cause extra material and time cost. Could also be an user setting or differently calculated. Idea is that if an overhang
// does not turn valid in double the amount of layers a slope of support angle would take to travel xy_distance, nothing reasonable will come from it. // does not turn valid in double the amount of layers a slope of support angle would take to travel xy_distance, nothing reasonable will come from it.
// The 2*z_distance_delta is only a catch for when the support angle is very high. // The 2*z_distance_delta is only a catch for when the support angle is very high.
// Used only if not min_xy_dist. // Used only if not min_xy_dist.
const coord_t max_overhang_insert_lag = config.z_distance_top_layers > 0 ? coord_t max_overhang_insert_lag = 0;
std::max<coord_t>(round_up_divide(config.xy_distance, max_overhang_speed / 2), 2 * config.z_distance_top_layers) : if (config.z_distance_top_layers > 0) {
0; max_overhang_insert_lag = 2 * config.z_distance_top_layers;
if (mesh_group_settings.support_angle > EPSILON && mesh_group_settings.support_angle < 0.5 * M_PI - EPSILON) {
//FIXME mesh_group_settings.support_angle does not apply to enforcers and also it does not apply to automatic support angle (by half the external perimeter width).
//used by max_overhang_insert_lag, only if not min_xy_dist.
const auto max_overhang_speed = coord_t(tan(mesh_group_settings.support_angle) * config.layer_height);
max_overhang_insert_lag = std::max(max_overhang_insert_lag, round_up_divide(config.xy_distance, max_overhang_speed / 2));
}
}
size_t num_support_layers; size_t num_support_layers;
int raft_contact_layer_idx; int raft_contact_layer_idx;
@ -1857,7 +1862,7 @@ static Point move_inside_if_outside(const Polygons &polygons, Point from, int di
} }
radius = config.getCollisionRadius(current_elem); radius = config.getCollisionRadius(current_elem);
const coord_t foot_radius_increase = config.branch_radius * (std::max(config.diameter_scale_bp_radius - config.diameter_angle_scale_factor, 0.0)); const coord_t foot_radius_increase = std::max(config.bp_radius_increase_per_layer - config.branch_radius_increase_per_layer, 0.0);
// Is nearly all of the time 1, but sometimes an increase of 1 could cause the radius to become bigger than recommendedMinRadius, // Is nearly all of the time 1, but sometimes an increase of 1 could cause the radius to become bigger than recommendedMinRadius,
// which could cause the radius to become bigger than precalculated. // which could cause the radius to become bigger than precalculated.
double planned_foot_increase = std::min(1.0, double(config.recommendedMinRadius(layer_idx - 1) - config.getRadius(current_elem)) / foot_radius_increase); double planned_foot_increase = std::min(1.0, double(config.recommendedMinRadius(layer_idx - 1) - config.getRadius(current_elem)) / foot_radius_increase);
@ -2015,9 +2020,9 @@ static void increase_areas_one_layer(
config.recommendedMinRadius(layer_idx - 1) < config.getRadius(elem.effective_radius_height + 1, elem.elephant_foot_increases)) { config.recommendedMinRadius(layer_idx - 1) < config.getRadius(elem.effective_radius_height + 1, elem.elephant_foot_increases)) {
// can guarantee elephant foot radius increase // can guarantee elephant foot radius increase
if (ceiled_parent_radius == volumes.ceilRadius(config.getRadius(parent.state.effective_radius_height + 1, parent.state.elephant_foot_increases + 1), parent.state.use_min_xy_dist)) if (ceiled_parent_radius == volumes.ceilRadius(config.getRadius(parent.state.effective_radius_height + 1, parent.state.elephant_foot_increases + 1), parent.state.use_min_xy_dist))
extra_speed += config.branch_radius * config.diameter_scale_bp_radius; extra_speed += config.bp_radius_increase_per_layer;
else else
extra_slow_speed += std::min(coord_t(config.branch_radius * config.diameter_scale_bp_radius), extra_slow_speed += std::min(coord_t(config.bp_radius_increase_per_layer),
config.maximum_move_distance - (config.maximum_move_distance_slow + extra_slow_speed)); config.maximum_move_distance - (config.maximum_move_distance_slow + extra_slow_speed));
} }
@ -2236,11 +2241,11 @@ static void increase_areas_one_layer(
out.to_model_gracious = first.to_model_gracious && second.to_model_gracious; // valid as we do not merge non-gracious with gracious out.to_model_gracious = first.to_model_gracious && second.to_model_gracious; // valid as we do not merge non-gracious with gracious
out.elephant_foot_increases = 0; out.elephant_foot_increases = 0;
if (config.diameter_scale_bp_radius > 0) { if (config.bp_radius_increase_per_layer > 0) {
coord_t foot_increase_radius = std::abs(std::max(config.getCollisionRadius(second), config.getCollisionRadius(first)) - config.getCollisionRadius(out)); coord_t foot_increase_radius = std::abs(std::max(config.getCollisionRadius(second), config.getCollisionRadius(first)) - config.getCollisionRadius(out));
// elephant_foot_increases has to be recalculated, as when a smaller tree with a larger elephant_foot_increases merge with a larger branch // elephant_foot_increases has to be recalculated, as when a smaller tree with a larger elephant_foot_increases merge with a larger branch
// the elephant_foot_increases may have to be lower as otherwise the radius suddenly increases. This results often in a non integer value. // the elephant_foot_increases may have to be lower as otherwise the radius suddenly increases. This results often in a non integer value.
out.elephant_foot_increases = foot_increase_radius / (config.branch_radius * (config.diameter_scale_bp_radius - config.diameter_angle_scale_factor)); out.elephant_foot_increases = foot_increase_radius / (config.bp_radius_increase_per_layer - config.branch_radius_increase_per_layer);
} }
// set last settings to the best out of both parents. If this is wrong, it will only cause a small performance penalty instead of weird behavior. // set last settings to the best out of both parents. If this is wrong, it will only cause a small performance penalty instead of weird behavior.
@ -3708,12 +3713,13 @@ static std::pair<int, int> discretize_circle(const Vec3f &center, const Vec3f &n
return { begin, int(pts.size()) }; return { begin, int(pts.size()) };
} }
static void extrude_branch( // Returns Z span of the generated mesh.
const std::vector<SupportElement*> &path, static std::pair<float, float> extrude_branch(
const TreeSupportSettings &config, const std::vector<const SupportElement*> &path,
const SlicingParameters &slicing_params, const TreeSupportSettings &config,
const std::vector<SupportElements> &move_bounds, const SlicingParameters &slicing_params,
indexed_triangle_set &result) const std::vector<SupportElements> &move_bounds,
indexed_triangle_set &result)
{ {
Vec3d p1, p2, p3; Vec3d p1, p2, p3;
Vec3d v1, v2; Vec3d v1, v2;
@ -3726,6 +3732,8 @@ static void extrude_branch(
// char fname[2048]; // char fname[2048];
// static int irun = 0; // static int irun = 0;
float zmin, zmax;
for (size_t ipath = 1; ipath < path.size(); ++ ipath) { for (size_t ipath = 1; ipath < path.size(); ++ ipath) {
const SupportElement &prev = *path[ipath - 1]; const SupportElement &prev = *path[ipath - 1];
const SupportElement &current = *path[ipath]; const SupportElement &current = *path[ipath];
@ -3742,6 +3750,7 @@ static void extrude_branch(
angle_step = M_PI / (2. * nsteps); angle_step = M_PI / (2. * nsteps);
int ifan = int(result.vertices.size()); int ifan = int(result.vertices.size());
result.vertices.emplace_back((p1 - nprev * radius).cast<float>()); result.vertices.emplace_back((p1 - nprev * radius).cast<float>());
zmin = result.vertices.back().z();
float angle = angle_step; float angle = angle_step;
for (int i = 1; i < nsteps; ++ i, angle += angle_step) { for (int i = 1; i < nsteps; ++ i, angle += angle_step) {
std::pair<int, int> strip = discretize_circle((p1 - nprev * radius * cos(angle)).cast<float>(), nprev.cast<float>(), radius * sin(angle), eps, result.vertices); std::pair<int, int> strip = discretize_circle((p1 - nprev * radius * cos(angle)).cast<float>(), nprev.cast<float>(), radius * sin(angle), eps, result.vertices);
@ -3772,6 +3781,7 @@ static void extrude_branch(
} }
int ifan = int(result.vertices.size()); int ifan = int(result.vertices.size());
result.vertices.emplace_back((p2 + ncurrent * radius).cast<float>()); result.vertices.emplace_back((p2 + ncurrent * radius).cast<float>());
zmax = result.vertices.back().z();
triangulate_fan<true>(result, ifan, prev_strip.first, prev_strip.second); triangulate_fan<true>(result, ifan, prev_strip.first, prev_strip.second);
// sprintf(fname, "d:\\temp\\meshes\\tree-partial-%d.obj", ++ irun); // sprintf(fname, "d:\\temp\\meshes\\tree-partial-%d.obj", ++ irun);
// its_write_obj(result, fname); // its_write_obj(result, fname);
@ -3799,6 +3809,8 @@ static void extrude_branch(
} }
#endif #endif
} }
return std::make_pair(zmin, zmax);
} }
#endif #endif
@ -4120,15 +4132,13 @@ static void organic_smooth_branches_avoid_collisions(
#endif // TREE_SUPPORT_ORGANIC_NUDGE_NEW #endif // TREE_SUPPORT_ORGANIC_NUDGE_NEW
// Organic specific: Smooth branches and produce one cummulative mesh to be sliced. // Organic specific: Smooth branches and produce one cummulative mesh to be sliced.
static indexed_triangle_set draw_branches( static std::vector<Polygons> draw_branches(
PrintObject &print_object, PrintObject &print_object,
const TreeModelVolumes &volumes, TreeModelVolumes &volumes,
const TreeSupportSettings &config, const TreeSupportSettings &config,
std::vector<SupportElements> &move_bounds, std::vector<SupportElements> &move_bounds,
std::function<void()> throw_on_cancel) std::function<void()> throw_on_cancel)
{ {
static int irun = 0;
// All SupportElements are put into a layer independent storage to improve parallelization. // All SupportElements are put into a layer independent storage to improve parallelization.
std::vector<std::pair<SupportElement*, int>> elements_with_link_down; std::vector<std::pair<SupportElement*, int>> elements_with_link_down;
std::vector<size_t> linear_data_layers; std::vector<size_t> linear_data_layers;
@ -4175,127 +4185,188 @@ static indexed_triangle_set draw_branches(
organic_smooth_branches_avoid_collisions(print_object, volumes, config, move_bounds, elements_with_link_down, linear_data_layers, throw_on_cancel); organic_smooth_branches_avoid_collisions(print_object, volumes, config, move_bounds, elements_with_link_down, linear_data_layers, throw_on_cancel);
// Reduce memory footprint. After this point only finalize_interface_and_support_areas() will use volumes and from that only collisions with zero radius will be used.
volumes.clear_all_but_object_collision();
// Unmark all nodes. // Unmark all nodes.
for (SupportElements &elements : move_bounds) for (SupportElements &elements : move_bounds)
for (SupportElement &element : elements) for (SupportElement &element : elements)
element.state.marked = false; element.state.marked = false;
// Traverse all nodes, generate tubes. // Traverse all nodes, generate tubes.
// Traversal stack with nodes and thier current parent // Traversal stack with nodes and their current parent
const SlicingParameters &slicing_params = print_object.slicing_parameters();
std::vector<SupportElement*> path;
indexed_triangle_set cummulative_mesh;
indexed_triangle_set partial_mesh;
indexed_triangle_set temp_mesh;
for (LayerIndex layer_idx = 0; layer_idx + 1 < LayerIndex(move_bounds.size()); ++ layer_idx) {
SupportElements &layer = move_bounds[layer_idx];
SupportElements &layer_above = move_bounds[layer_idx + 1];
for (SupportElement &start_element : layer) struct Branch {
if (! start_element.state.marked && ! start_element.parents.empty()) { std::vector<const SupportElement*> path;
// Collect elements up to a bifurcation above. bool has_root{ false };
start_element.state.marked = true; bool has_tip { false };
for (size_t parent_idx = 0; parent_idx < start_element.parents.size(); ++ parent_idx) { };
path.clear();
path.emplace_back(&start_element); struct Slice {
// Traverse each branch until it branches again. Polygons polygons;
SupportElement &first_parent = layer_above[start_element.parents[parent_idx]]; size_t num_branches{ 0 };
assert(path.back()->state.layer_idx + 1 == first_parent.state.layer_idx); };
path.emplace_back(&first_parent);
if (first_parent.parents.size() < 2) struct Tree {
first_parent.state.marked = true; std::vector<Branch> branches;
if (first_parent.parents.size() == 1) {
for (SupportElement *parent = &first_parent;;) { std::vector<Slice> slices;
SupportElement &next_parent = move_bounds[parent->state.layer_idx + 1][parent->parents.front()]; LayerIndex first_layer_id{ -1 };
assert(path.back()->state.layer_idx + 1 == next_parent.state.layer_idx); };
path.emplace_back(&next_parent);
if (next_parent.parents.size() > 1) std::vector<Tree> trees;
break;
next_parent.state.marked = true; struct TreeVisitor {
if (next_parent.parents.size() == 0) static void visit_recursive(std::vector<SupportElements> &move_bounds, SupportElement &start_element, Tree &out) {
break; assert(! start_element.state.marked && ! start_element.parents.empty());
parent = &next_parent; // Collect elements up to a bifurcation above.
start_element.state.marked = true;
// For each branch bifurcating from this point:
SupportElements &layer = move_bounds[start_element.state.layer_idx];
SupportElements &layer_above = move_bounds[start_element.state.layer_idx + 1];
bool root = out.branches.empty();
for (size_t parent_idx = 0; parent_idx < start_element.parents.size(); ++ parent_idx) {
Branch branch;
branch.path.emplace_back(&start_element);
// Traverse each branch until it branches again.
SupportElement &first_parent = layer_above[start_element.parents[parent_idx]];
assert(branch.path.back()->state.layer_idx + 1 == first_parent.state.layer_idx);
branch.path.emplace_back(&first_parent);
if (first_parent.parents.size() < 2)
first_parent.state.marked = true;
SupportElement *next_branch = nullptr;
if (first_parent.parents.size() == 1)
for (SupportElement *parent = &first_parent;;) {
SupportElement &next_parent = move_bounds[parent->state.layer_idx + 1][parent->parents.front()];
assert(branch.path.back()->state.layer_idx + 1 == next_parent.state.layer_idx);
branch.path.emplace_back(&next_parent);
if (next_parent.parents.size() > 1) {
next_branch = &next_parent;
break;
} }
next_parent.state.marked = true;
if (next_parent.parents.size() == 0)
break;
parent = &next_parent;
} }
assert(branch.path.size() >= 2);
branch.has_root = root;
branch.has_tip = ! next_branch;
out.branches.emplace_back(std::move(branch));
if (next_branch)
visit_recursive(move_bounds, *next_branch, out);
}
}
};
for (LayerIndex layer_idx = 0; layer_idx + 1 < LayerIndex(move_bounds.size()); ++ layer_idx)
for (SupportElement &start_element : move_bounds[layer_idx])
if (! start_element.state.marked && ! start_element.parents.empty()) {
trees.push_back({});
TreeVisitor::visit_recursive(move_bounds, start_element, trees.back());
assert(! trees.back().branches.empty());
}
const SlicingParameters &slicing_params = print_object.slicing_parameters();
MeshSlicingParams mesh_slicing_params;
mesh_slicing_params.mode = MeshSlicingParams::SlicingMode::Positive;
tbb::parallel_for(tbb::blocked_range<size_t>(0, trees.size()),
[&trees, &config, &slicing_params, &move_bounds, &mesh_slicing_params, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
indexed_triangle_set partial_mesh;
std::vector<float> slice_z;
for (size_t tree_id = range.begin(); tree_id < range.end(); ++ tree_id) {
Tree &tree = trees[tree_id];
for (const Branch &branch : tree.branches) {
// Triangulate the tube. // Triangulate the tube.
partial_mesh.clear(); partial_mesh.clear();
extrude_branch(path, config, slicing_params, move_bounds, partial_mesh); std::pair<float, float> zspan = extrude_branch(branch.path, config, slicing_params, move_bounds, partial_mesh);
#if 0 LayerIndex layer_begin = branch.has_root ?
{ branch.path.front()->state.layer_idx :
char fname[2048]; std::min(branch.path.front()->state.layer_idx, layer_idx_ceil(slicing_params, config, zspan.first));
static int irun = 0; LayerIndex layer_end = (branch.has_tip ?
sprintf(fname, "d:\\temp\\meshes\\tree-raw-%d.obj", ++ irun); branch.path.back()->state.layer_idx :
its_write_obj(partial_mesh, fname); std::max(branch.path.back()->state.layer_idx, layer_idx_floor(slicing_params, config, zspan.second))) + 1;
#if 0 slice_z.clear();
temp_mesh.clear(); for (LayerIndex layer_idx = layer_begin; layer_idx < layer_end; ++ layer_idx) {
cut_mesh(partial_mesh, layer_z(slicing_params, path.back()->state.layer_idx) + EPSILON, nullptr, &temp_mesh, false); const double print_z = layer_z(slicing_params, config, layer_idx);
sprintf(fname, "d:\\temp\\meshes\\tree-trimmed1-%d.obj", irun); const double bottom_z = layer_idx > 0 ? layer_z(slicing_params, config, layer_idx - 1) : 0.;
its_write_obj(temp_mesh, fname); slice_z.emplace_back(float(0.5 * (bottom_z + print_z)));
partial_mesh.clear();
cut_mesh(temp_mesh, layer_z(slicing_params, path.front()->state.layer_idx) - EPSILON, &partial_mesh, nullptr, false);
sprintf(fname, "d:\\temp\\meshes\\tree-trimmed2-%d.obj", irun);
#endif
its_write_obj(partial_mesh, fname);
} }
#endif std::vector<Polygons> slices = slice_mesh(partial_mesh, slice_z, mesh_slicing_params, throw_on_cancel);
its_merge(cummulative_mesh, partial_mesh); size_t num_empty = std::find_if(slices.begin(), slices.end(), [](auto &s) { return !s.empty(); }) - slices.begin();
layer_begin += LayerIndex(num_empty);
for (; slices.back().empty(); -- layer_end);
LayerIndex new_begin = tree.first_layer_id == -1 ? layer_begin : std::min(tree.first_layer_id, layer_begin);
LayerIndex new_end = tree.first_layer_id == -1 ? layer_end : std::max(tree.first_layer_id + LayerIndex(tree.slices.size()), layer_end);
size_t new_size = size_t(new_end - new_begin);
if (tree.first_layer_id == -1) {
} else if (tree.slices.capacity() < new_size) {
std::vector<Slice> new_slices;
new_slices.reserve(new_size);
if (LayerIndex dif = tree.first_layer_id - new_begin; dif > 0)
new_slices.insert(new_slices.end(), dif, {});
append(new_slices, std::move(tree.slices));
tree.slices.swap(new_slices);
} else if (LayerIndex dif = tree.first_layer_id - new_begin; dif > 0)
tree.slices.insert(tree.slices.begin(), tree.first_layer_id - new_begin, {});
tree.slices.insert(tree.slices.end(), new_size - tree.slices.size(), {});
layer_begin -= LayerIndex(num_empty);
for (LayerIndex i = layer_begin; i != layer_end; ++ i)
if (Polygons &src = slices[i - layer_begin]; ! src.empty()) {
Slice &dst = tree.slices[i - new_begin];
if (++ dst.num_branches > 1)
append(dst.polygons, std::move(src));
else
dst.polygons = std::move(std::move(src));
}
tree.first_layer_id = new_begin;
} }
throw_on_cancel();
} }
}
return cummulative_mesh;
}
// Organic specific: Slice the cummulative mesh produced by draw_branches().
static void slice_branches(
PrintObject &print_object,
const TreeModelVolumes &volumes,
const TreeSupportSettings &config,
const std::vector<Polygons> &overhangs,
std::vector<SupportElements> &move_bounds,
const indexed_triangle_set &cummulative_mesh,
SupportGeneratorLayersPtr &bottom_contacts,
SupportGeneratorLayersPtr &top_contacts,
SupportGeneratorLayersPtr &intermediate_layers,
SupportGeneratorLayerStorage &layer_storage,
std::function<void()> throw_on_cancel)
{
const SlicingParameters &slicing_params = print_object.slicing_parameters();
std::vector<float> slice_z;
for (size_t layer_idx = 0; layer_idx < move_bounds.size(); ++ layer_idx) {
const double print_z = layer_z(print_object.slicing_parameters(), config, layer_idx);
const double bottom_z = layer_idx > 0 ? layer_z(print_object.slicing_parameters(), config, layer_idx - 1) : 0.;
slice_z.emplace_back(float(0.5 * (bottom_z + print_z)));
}
// Remove the trailing slices.
while (! slice_z.empty())
if (move_bounds[slice_z.size() - 1].empty())
slice_z.pop_back();
else
break;
#if 0
its_write_obj(cummulative_mesh, "d:\\temp\\meshes\\tree.obj");
#endif
MeshSlicingParamsEx params;
params.closing_radius = float(print_object.config().slice_closing_radius.value);
params.mode = MeshSlicingParams::SlicingMode::Positive;
std::vector<ExPolygons> slices = slice_mesh_ex(cummulative_mesh, slice_z, params, throw_on_cancel);
// Trim the slices.
std::vector<Polygons> support_layer_storage(move_bounds.size());
tbb::parallel_for(tbb::blocked_range<size_t>(0, slices.size()),
[&](const tbb::blocked_range<size_t> &range) {
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx)
if (ExPolygons &src = slices[layer_idx]; ! src.empty())
support_layer_storage[layer_idx] = diff_clipped(to_polygons(std::move(src)), volumes.getCollision(0, layer_idx, true));
}); });
std::vector<Polygons> support_roof_storage(move_bounds.size()); tbb::parallel_for(tbb::blocked_range<size_t>(0, trees.size()),
finalize_interface_and_support_areas(print_object, volumes, config, overhangs, support_layer_storage, support_roof_storage, [&trees, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
bottom_contacts, top_contacts, intermediate_layers, layer_storage, throw_on_cancel); for (size_t tree_id = range.begin(); tree_id < range.end(); ++ tree_id) {
Tree &tree = trees[tree_id];
for (Slice &slice : tree.slices)
if (slice.num_branches > 1) {
slice.polygons = union_(slice.polygons);
slice.num_branches = 1;
}
throw_on_cancel();
}
});
size_t num_layers = 0;
for (Tree &tree : trees)
if (tree.first_layer_id >= 0)
num_layers = std::max(num_layers, size_t(tree.first_layer_id + tree.slices.size()));
std::vector<Slice> slices(num_layers, Slice{});
for (Tree &tree : trees)
if (tree.first_layer_id >= 0) {
for (LayerIndex i = tree.first_layer_id; i != tree.first_layer_id + LayerIndex(tree.slices.size()); ++ i)
if (Slice &src = tree.slices[i - tree.first_layer_id]; ! src.polygons.empty()) {
Slice &dst = slices[i];
if (++ dst.num_branches > 1)
append(dst.polygons, std::move(src.polygons));
else
dst.polygons = std::move(src.polygons);
}
}
std::vector<Polygons> support_layer_storage(move_bounds.size());
tbb::parallel_for(tbb::blocked_range<size_t>(0, std::min(move_bounds.size(), slices.size())),
[&slices, &support_layer_storage, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
for (size_t slice_id = range.begin(); slice_id < range.end(); ++ slice_id) {
Slice &slice = slices[slice_id];
support_layer_storage[slice_id] = slice.num_branches > 1 ? union_(slice.polygons) : std::move(slice.polygons);
throw_on_cancel();
}
});
//FIXME simplify!
return support_layer_storage;
} }
/*! /*!
@ -4412,10 +4483,9 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume
bottom_contacts, top_contacts, intermediate_layers, layer_storage, throw_on_cancel); bottom_contacts, top_contacts, intermediate_layers, layer_storage, throw_on_cancel);
else { else {
assert(print_object.config().support_material_style == smsOrganic); assert(print_object.config().support_material_style == smsOrganic);
indexed_triangle_set branches = draw_branches(*print.get_object(processing.second.front()), volumes, config, move_bounds, throw_on_cancel); std::vector<Polygons> support_layer_storage = draw_branches(*print.get_object(processing.second.front()), volumes, config, move_bounds, throw_on_cancel);
// Reduce memory footprint. After this point only slice_branches() will use volumes and from that only collisions with zero radius will be used. std::vector<Polygons> support_roof_storage(support_layer_storage.size());
volumes.clear_all_but_object_collision(); finalize_interface_and_support_areas(print_object, volumes, config, overhangs, support_layer_storage, support_roof_storage,
slice_branches(*print.get_object(processing.second.front()), volumes, config, overhangs, move_bounds, branches,
bottom_contacts, top_contacts, intermediate_layers, layer_storage, throw_on_cancel); bottom_contacts, top_contacts, intermediate_layers, layer_storage, throw_on_cancel);
} }

View File

@ -302,9 +302,9 @@ public:
*/ */
size_t tip_layers; size_t tip_layers;
/*! /*!
* \brief Factor by which to increase the branch radius. * \brief How much a branch radius increases with each layer to guarantee the prescribed tree widening.
*/ */
double diameter_angle_scale_factor; double branch_radius_increase_per_layer;
/*! /*!
* \brief How much a branch resting on the model may grow in radius by merging with branches that can reach the buildplate. * \brief How much a branch resting on the model may grow in radius by merging with branches that can reach the buildplate.
*/ */
@ -330,17 +330,18 @@ public:
*/ */
coord_t xy_distance; coord_t xy_distance;
/*! /*!
* \brief Radius a branch should have when reaching the buildplate. * \brief A minimum radius a tree trunk should expand to at the buildplate if possible.
*/ */
coord_t bp_radius; coord_t bp_radius;
/*! /*!
* \brief The layer index at which an increase in radius may be required to reach the bp_radius. * \brief The layer index at which an increase in radius may be required to reach the bp_radius.
*/ */
coord_t layer_start_bp_radius; LayerIndex layer_start_bp_radius;
/*! /*!
* \brief Factor by which to increase the branch radius to reach the required bp_radius at layer 0. Note that this radius increase will not happen in the tip, to ensure the tip is structurally sound. * \brief How much one is allowed to increase the tree branch radius close to print bed to reach the required bp_radius at layer 0.
* Note that this radius increase will not happen in the tip, to ensure the tip is structurally sound.
*/ */
double diameter_scale_bp_radius; double bp_radius_increase_per_layer;
/*! /*!
* \brief minimum xy_distance. Only relevant when Z overrides XY, otherwise equal to xy_distance- * \brief minimum xy_distance. Only relevant when Z overrides XY, otherwise equal to xy_distance-
*/ */
@ -418,7 +419,9 @@ public:
public: public:
bool operator==(const TreeSupportSettings& other) const bool operator==(const TreeSupportSettings& other) const
{ {
return branch_radius == other.branch_radius && tip_layers == other.tip_layers && diameter_angle_scale_factor == other.diameter_angle_scale_factor && layer_start_bp_radius == other.layer_start_bp_radius && bp_radius == other.bp_radius && diameter_scale_bp_radius == other.diameter_scale_bp_radius && min_radius == other.min_radius && xy_min_distance == other.xy_min_distance && // as a recalculation of the collision areas is required to set a new min_radius. return branch_radius == other.branch_radius && tip_layers == other.tip_layers && branch_radius_increase_per_layer == other.branch_radius_increase_per_layer && layer_start_bp_radius == other.layer_start_bp_radius && bp_radius == other.bp_radius &&
// as a recalculation of the collision areas is required to set a new min_radius.
bp_radius_increase_per_layer == other.bp_radius_increase_per_layer && min_radius == other.min_radius && xy_min_distance == other.xy_min_distance &&
xy_distance - xy_min_distance == other.xy_distance - other.xy_min_distance && // if the delta of xy_min_distance and xy_distance is different the collision areas have to be recalculated. xy_distance - xy_min_distance == other.xy_distance - other.xy_min_distance && // if the delta of xy_min_distance and xy_distance is different the collision areas have to be recalculated.
support_rests_on_model == other.support_rests_on_model && increase_radius_until_layer == other.increase_radius_until_layer && min_dtt_to_model == other.min_dtt_to_model && max_to_model_radius_increase == other.max_to_model_radius_increase && maximum_move_distance == other.maximum_move_distance && maximum_move_distance_slow == other.maximum_move_distance_slow && z_distance_bottom_layers == other.z_distance_bottom_layers && support_line_width == other.support_line_width && support_rests_on_model == other.support_rests_on_model && increase_radius_until_layer == other.increase_radius_until_layer && min_dtt_to_model == other.min_dtt_to_model && max_to_model_radius_increase == other.max_to_model_radius_increase && maximum_move_distance == other.maximum_move_distance && maximum_move_distance_slow == other.maximum_move_distance_slow && z_distance_bottom_layers == other.z_distance_bottom_layers && support_line_width == other.support_line_width &&
support_line_spacing == other.support_line_spacing && support_roof_line_width == other.support_roof_line_width && // can not be set on a per-mesh basis currently, so code to enable processing different roof line width in the same iteration seems useless. support_line_spacing == other.support_line_spacing && support_roof_line_width == other.support_roof_line_width && // can not be set on a per-mesh basis currently, so code to enable processing different roof line width in the same iteration seems useless.
@ -470,9 +473,9 @@ public:
{ {
return (distance_to_top <= tip_layers ? min_radius + (branch_radius - min_radius) * distance_to_top / tip_layers : // tip return (distance_to_top <= tip_layers ? min_radius + (branch_radius - min_radius) * distance_to_top / tip_layers : // tip
branch_radius + // base branch_radius + // base
branch_radius * (distance_to_top - tip_layers) * diameter_angle_scale_factor) (distance_to_top - tip_layers) * branch_radius_increase_per_layer)
+ // gradual increase + // gradual increase
branch_radius * elephant_foot_increases * (std::max(diameter_scale_bp_radius - diameter_angle_scale_factor, 0.0)); elephant_foot_increases * (std::max(bp_radius_increase_per_layer - branch_radius_increase_per_layer, 0.0));
} }
/*! /*!
@ -502,8 +505,8 @@ public:
*/ */
[[nodiscard]] inline coord_t recommendedMinRadius(LayerIndex layer_idx) const [[nodiscard]] inline coord_t recommendedMinRadius(LayerIndex layer_idx) const
{ {
double scale = (layer_start_bp_radius - int(layer_idx)) * diameter_scale_bp_radius; double num_layers_widened = layer_start_bp_radius - layer_idx;
return scale > 0 ? branch_radius + branch_radius * scale : 0; return num_layers_widened > 0 ? branch_radius + num_layers_widened * bp_radius_increase_per_layer : 0;
} }
/*! /*!

View File

@ -1911,6 +1911,26 @@ std::vector<ExPolygons> slice_mesh_ex(
this_mode == MeshSlicingParams::SlicingMode::EvenOdd ? ClipperLib::pftEvenOdd : this_mode == MeshSlicingParams::SlicingMode::EvenOdd ? ClipperLib::pftEvenOdd :
this_mode == MeshSlicingParams::SlicingMode::PositiveLargestContour ? ClipperLib::pftPositive : ClipperLib::pftNonZero, this_mode == MeshSlicingParams::SlicingMode::PositiveLargestContour ? ClipperLib::pftPositive : ClipperLib::pftNonZero,
&expolygons); &expolygons);
#if 0
//#ifndef _NDEBUG
// Test whether the expolygons in a single layer overlap.
for (size_t i = 0; i < expolygons.size(); ++ i)
for (size_t j = i + 1; j < expolygons.size(); ++ j) {
Polygons overlap = intersection(expolygons[i], expolygons[j]);
assert(overlap.empty());
}
#endif
#if 0
//#ifndef _NDEBUG
for (const ExPolygon &ex : expolygons) {
assert(! has_duplicate_points(ex.contour));
for (const Polygon &hole : ex.holes)
assert(! has_duplicate_points(hole));
assert(! has_duplicate_points(ex));
}
assert(!has_duplicate_points(expolygons));
#endif // _NDEBUG
//FIXME simplify //FIXME simplify
if (this_mode == MeshSlicingParams::SlicingMode::PositiveLargestContour) if (this_mode == MeshSlicingParams::SlicingMode::PositiveLargestContour)
keep_largest_contour_only(expolygons); keep_largest_contour_only(expolygons);
@ -1921,6 +1941,16 @@ std::vector<ExPolygons> slice_mesh_ex(
append(simplified, ex.simplify(resolution)); append(simplified, ex.simplify(resolution));
expolygons = std::move(simplified); expolygons = std::move(simplified);
} }
#if 0
//#ifndef _NDEBUG
for (const ExPolygon &ex : expolygons) {
assert(! has_duplicate_points(ex.contour));
for (const Polygon &hole : ex.holes)
assert(! has_duplicate_points(hole));
assert(! has_duplicate_points(ex));
}
assert(! has_duplicate_points(expolygons));
#endif // _NDEBUG
} }
}); });
// BOOST_LOG_TRIVIAL(debug) << "slice_mesh make_expolygons in parallel - end"; // BOOST_LOG_TRIVIAL(debug) << "slice_mesh make_expolygons in parallel - end";

View File

@ -106,8 +106,8 @@ enum Axis {
NUM_AXES_WITH_UNKNOWN, NUM_AXES_WITH_UNKNOWN,
}; };
template <typename T> template <typename T, typename Alloc, typename Alloc2>
inline void append(std::vector<T>& dest, const std::vector<T>& src) inline void append(std::vector<T, Alloc> &dest, const std::vector<T, Alloc2> &src)
{ {
if (dest.empty()) if (dest.empty())
dest = src; // copy dest = src; // copy
@ -115,8 +115,8 @@ inline void append(std::vector<T>& dest, const std::vector<T>& src)
dest.insert(dest.end(), src.begin(), src.end()); dest.insert(dest.end(), src.begin(), src.end());
} }
template <typename T> template <typename T, typename Alloc>
inline void append(std::vector<T>& dest, std::vector<T>&& src) inline void append(std::vector<T, Alloc> &dest, std::vector<T, Alloc> &&src)
{ {
if (dest.empty()) if (dest.empty())
dest = std::move(src); dest = std::move(src);

View File

@ -250,6 +250,8 @@ set(SLIC3R_GUI_SOURCES
Utils/Http.hpp Utils/Http.hpp
Utils/FixModelByWin10.cpp Utils/FixModelByWin10.cpp
Utils/FixModelByWin10.hpp Utils/FixModelByWin10.hpp
Utils/Mainsail.cpp
Utils/Mainsail.hpp
Utils/OctoPrint.cpp Utils/OctoPrint.cpp
Utils/OctoPrint.hpp Utils/OctoPrint.hpp
Utils/Duet.cpp Utils/Duet.cpp

View File

@ -593,7 +593,7 @@ void GLVolumeCollection::load_object_auxiliary(
return; return;
const Transform3d mesh_trafo_inv = print_object->trafo().inverse(); const Transform3d mesh_trafo_inv = print_object->trafo().inverse();
auto add_volume = [this, &instances, timestamp](int obj_idx, int inst_idx, const ModelInstance& model_instance, SLAPrintObjectStep step, auto add_volume = [this, timestamp](int obj_idx, int inst_idx, const ModelInstance& model_instance, SLAPrintObjectStep step,
const TriangleMesh& mesh, const ColorRGBA& color, std::optional<const TriangleMesh> convex_hull = std::nullopt) { const TriangleMesh& mesh, const ColorRGBA& color, std::optional<const TriangleMesh> convex_hull = std::nullopt) {
if (mesh.empty()) if (mesh.empty())
return; return;

View File

@ -3171,6 +3171,8 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
} }
else { else {
auto changed = [app_config, &appconfig_new = std::as_const(this->appconfig_new)](const std::string& section_name) { auto changed = [app_config, &appconfig_new = std::as_const(this->appconfig_new)](const std::string& section_name) {
if (!appconfig_new.has_section(section_name))
return false;
return (app_config->has_section(section_name) ? app_config->get_section(section_name) : std::map<std::string, std::string>()) != appconfig_new.get_section(section_name); return (app_config->has_section(section_name) ? app_config->get_section(section_name) : std::map<std::string, std::string>()) != appconfig_new.get_section(section_name);
}; };
bool is_filaments_changed = changed(AppConfig::SECTION_FILAMENTS); bool is_filaments_changed = changed(AppConfig::SECTION_FILAMENTS);

View File

@ -697,7 +697,9 @@ void GCodeViewer::init()
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Line; buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Line;
buffer.vertices.format = VBuffer::EFormat::Position; buffer.vertices.format = VBuffer::EFormat::Position;
#if ENABLE_GL_CORE_PROFILE #if ENABLE_GL_CORE_PROFILE
buffer.shader = OpenGLManager::get_gl_info().is_core_profile() ? "dashed_thick_lines" : "flat"; // on MAC using the geometry shader of dashed_thick_lines is too slow
buffer.shader = "flat";
// buffer.shader = OpenGLManager::get_gl_info().is_core_profile() ? "dashed_thick_lines" : "flat";
#else #else
buffer.shader = "flat"; buffer.shader = "flat";
#endif // ENABLE_GL_CORE_PROFILE #endif // ENABLE_GL_CORE_PROFILE
@ -836,7 +838,8 @@ void GCodeViewer::refresh(const GCodeProcessorResult& gcode_result, const std::v
case EMoveType::Extrude: case EMoveType::Extrude:
{ {
m_extrusions.ranges.height.update_from(round_to_bin(curr.height)); m_extrusions.ranges.height.update_from(round_to_bin(curr.height));
m_extrusions.ranges.width.update_from(round_to_bin(curr.width)); if (curr.extrusion_role != GCodeExtrusionRole::Custom || is_visible(GCodeExtrusionRole::Custom))
m_extrusions.ranges.width.update_from(round_to_bin(curr.width));
m_extrusions.ranges.fan_speed.update_from(curr.fan_speed); m_extrusions.ranges.fan_speed.update_from(curr.fan_speed);
m_extrusions.ranges.temperature.update_from(curr.temperature); m_extrusions.ranges.temperature.update_from(curr.temperature);
if (curr.extrusion_role != GCodeExtrusionRole::Custom || is_visible(GCodeExtrusionRole::Custom)) if (curr.extrusion_role != GCodeExtrusionRole::Custom || is_visible(GCodeExtrusionRole::Custom))
@ -3944,6 +3947,20 @@ void GCodeViewer::render_legend(float& legend_height)
} }
} }
if (m_view_type == EViewType::Width || m_view_type == EViewType::VolumetricRate) {
const auto custom_it = std::find(m_roles.begin(), m_roles.end(), GCodeExtrusionRole::Custom);
if (custom_it != m_roles.end()) {
const bool custom_visible = is_visible(GCodeExtrusionRole::Custom);
const wxString btn_text = custom_visible ? _u8L("Hide Custom GCode") : _u8L("Show Custom GCode");
ImGui::Separator();
if (imgui.button(btn_text, ImVec2(-1.0f, 0.0f), true)) {
m_extrusions.role_visibility_flags = custom_visible ? m_extrusions.role_visibility_flags & ~(1 << int(GCodeExtrusionRole::Custom)) :
m_extrusions.role_visibility_flags | (1 << int(GCodeExtrusionRole::Custom));
wxGetApp().plater()->refresh_print();
}
}
}
// total estimated printing time section // total estimated printing time section
if (show_estimated_time) { if (show_estimated_time) {
ImGui::Spacing(); ImGui::Spacing();

Some files were not shown because too many files have changed in this diff Show More