Fixed conflicts after merge with master
This commit is contained in:
commit
159e699cd5
@ -406,6 +406,7 @@ endif()
|
||||
set(TBB_DEBUG 1)
|
||||
find_package(TBB REQUIRED)
|
||||
slic3r_remap_configs(TBB::tbb RelWithDebInfo Release)
|
||||
slic3r_remap_configs(TBB::tbbmalloc RelWithDebInfo Release)
|
||||
# include_directories(${TBB_INCLUDE_DIRS})
|
||||
# add_definitions(${TBB_DEFINITIONS})
|
||||
# if(MSVC)
|
||||
|
@ -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
|
||||
0.2.1 Added Eolas Prints filaments.
|
||||
0.2.0 Added Photon Mono X printer.
|
||||
|
@ -73,6 +73,13 @@ technology = FFF
|
||||
family = 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]
|
||||
name = Photon Mono X
|
||||
variants = default
|
||||
@ -80,6 +87,13 @@ technology = SLA
|
||||
family = PHOTON MONO
|
||||
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
|
||||
# not make it into the user interface.
|
||||
|
||||
@ -2327,11 +2341,10 @@ z_offset = 0
|
||||
## SLA printers
|
||||
|
||||
[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
|
||||
output_filename_format = [input_filename_base].pwmx
|
||||
pad_edge_radius = 0.5
|
||||
pad_enable = 0
|
||||
pad_enable = 1
|
||||
pad_max_merge_distance = 50
|
||||
pad_wall_height = 0
|
||||
pad_wall_thickness = 1
|
||||
@ -2355,20 +2368,38 @@ support_pillar_widening_factor = 0
|
||||
supports_enable = 1
|
||||
support_small_pillar_diameter_percent = 60%
|
||||
|
||||
[sla_print:0.05 Normal @ANYCUBIC]
|
||||
[sla_print:0.05 Normal @ANYCUBIC ABSTRACT]
|
||||
inherits = *common print ANYCUBIC SLA*
|
||||
compatible_printers_condition = printer_notes=~/.*ABSTRACT_ONLY.*/
|
||||
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
|
||||
|
||||
#MONO series printer need a significantly reduced exposure time but are otherwise compatible
|
||||
[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
|
||||
exposure_time = 7
|
||||
initial_exposure_time = 40
|
||||
initial_layer_height = 0.05
|
||||
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*]
|
||||
inherits = *common ANYCUBIC SLA*
|
||||
@ -2380,10 +2411,66 @@ initial_exposure_time = 40
|
||||
material_type = Tough
|
||||
material_vendor = Generic
|
||||
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
|
||||
|
||||
[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_technology = SLA
|
||||
printer_model = PHOTON MONO X
|
||||
@ -2410,4 +2497,4 @@ 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_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
|
BIN
resources/profiles/Anycubic/PHOTON MONO SE_thumbnail.png
Normal file
BIN
resources/profiles/Anycubic/PHOTON MONO SE_thumbnail.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 38 KiB |
BIN
resources/profiles/Anycubic/PHOTON MONO_thumbnail.png
Normal file
BIN
resources/profiles/Anycubic/PHOTON MONO_thumbnail.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 35 KiB |
@ -1,4 +1,7 @@
|
||||
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-alpha0 Updated output filename format.
|
||||
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-alpha1 Updated FW version notification. Decreased min layer time for PLA.
|
||||
1.6.0-alpha0 Default top fill set to monotonic lines. Updated infill/perimeter overlap values. Updated output filename format. Enabled dynamic overhang speeds.
|
||||
min_slic3r_version = 2.5.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
|
||||
1.6.4 Fixed compatibility condition for MMU1 filaments.
|
||||
1.6.3 Added SLA materials.
|
||||
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.
|
||||
|
@ -5,7 +5,7 @@
|
||||
name = Prusa Research
|
||||
# Configuration version of this file. Config file will only be installed, if the config_version differs.
|
||||
# This means, the server may force the PrusaSlicer configuration to be downgraded.
|
||||
config_version = 1.9.0-alpha1
|
||||
config_version = 1.9.0-alpha4
|
||||
# Where to get the updates from?
|
||||
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/
|
||||
changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1%
|
||||
@ -2130,6 +2130,7 @@ default_acceleration = 1250
|
||||
max_print_speed = 200
|
||||
first_layer_extrusion_width = 0.5
|
||||
support_material_extrusion_width = 0.37
|
||||
top_infill_extrusion_width = 0.4
|
||||
gcode_resolution = 0.008
|
||||
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
|
||||
gcode_resolution = 0.008
|
||||
support_material_extrusion_width = 0.37
|
||||
top_infill_extrusion_width = 0.4
|
||||
compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.4
|
||||
|
||||
[print:0.20mm SPEED @XL 0.4]
|
||||
@ -2192,6 +2194,7 @@ default_acceleration = 1250
|
||||
max_print_speed = 200
|
||||
first_layer_extrusion_width = 0.5
|
||||
support_material_extrusion_width = 0.37
|
||||
top_infill_extrusion_width = 0.42
|
||||
compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.4
|
||||
|
||||
[print:0.30mm DRAFT @XL 0.4]
|
||||
@ -3036,7 +3039,7 @@ inherits = *0.15mm*; *MK4*
|
||||
perimeter_speed = 45
|
||||
external_perimeter_speed = 25
|
||||
small_perimeter_speed = 25
|
||||
infill_speed = 90
|
||||
infill_speed = 120
|
||||
solid_infill_speed = 90
|
||||
top_solid_infill_speed = 40
|
||||
support_material_contact_distance = 0.2
|
||||
@ -3059,6 +3062,7 @@ max_print_speed = 200
|
||||
first_layer_extrusion_width = 0.5
|
||||
support_material_extrusion_width = 0.37
|
||||
gcode_resolution = 0.008
|
||||
top_infill_extrusion_width = 0.4
|
||||
compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.4
|
||||
|
||||
[print:0.15mm SPEED @MK4 0.4]
|
||||
@ -3089,6 +3093,7 @@ max_print_speed = 200
|
||||
first_layer_extrusion_width = 0.5
|
||||
support_material_extrusion_width = 0.37
|
||||
gcode_resolution = 0.008
|
||||
top_infill_extrusion_width = 0.42
|
||||
compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.4
|
||||
|
||||
[print:0.20mm QUALITY @MK4 0.4]
|
||||
@ -3096,7 +3101,7 @@ inherits = *0.20mm*; *MK4*
|
||||
perimeter_speed = 45
|
||||
external_perimeter_speed = 25
|
||||
small_perimeter_speed = 25
|
||||
infill_speed = 90
|
||||
infill_speed = 120
|
||||
solid_infill_speed = 90
|
||||
top_solid_infill_speed = 40
|
||||
support_material_contact_distance = 0.2
|
||||
@ -3119,6 +3124,7 @@ max_print_speed = 200
|
||||
first_layer_extrusion_width = 0.5
|
||||
gcode_resolution = 0.008
|
||||
support_material_extrusion_width = 0.37
|
||||
top_infill_extrusion_width = 0.4
|
||||
compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.4
|
||||
|
||||
[print:0.20mm SPEED @MK4 0.4]
|
||||
@ -3149,6 +3155,7 @@ default_acceleration = 1000
|
||||
max_print_speed = 200
|
||||
first_layer_extrusion_width = 0.5
|
||||
support_material_extrusion_width = 0.37
|
||||
top_infill_extrusion_width = 0.42
|
||||
compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==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
|
||||
compatible_printers =
|
||||
# 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"
|
||||
extrusion_multiplier = 1
|
||||
filament_loading_speed = 28
|
||||
@ -3608,7 +3615,7 @@ min_fan_speed = 100
|
||||
temperature = 210
|
||||
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"
|
||||
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*]
|
||||
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
|
||||
filament_retract_length = 1
|
||||
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*]
|
||||
inherits = *PET*
|
||||
@ -3700,7 +3707,7 @@ slowdown_below_layer_time = 18
|
||||
filament_retract_length = 0.8
|
||||
|
||||
[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*]
|
||||
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*]
|
||||
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"
|
||||
filament_cooling_final_speed = 50
|
||||
filament_cooling_initial_speed = 10
|
||||
@ -3869,7 +3876,7 @@ compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.6
|
||||
|
||||
[filament:*PC08PG*]
|
||||
inherits = *PCPG*
|
||||
filament_max_volumetric_speed = 20
|
||||
filament_max_volumetric_speed = 18
|
||||
compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.8
|
||||
|
||||
[filament:*PCMK4*]
|
||||
@ -3885,7 +3892,7 @@ compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.6
|
||||
|
||||
[filament:*PC08MK4*]
|
||||
inherits = *PCMK4*
|
||||
filament_max_volumetric_speed = 20
|
||||
filament_max_volumetric_speed = 18
|
||||
compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.8
|
||||
|
||||
[filament:*PAPG*]
|
||||
@ -4196,7 +4203,7 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no
|
||||
temperature = 260
|
||||
filament_retract_length = nil
|
||||
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]
|
||||
inherits = ColorFabb XT-CF20; *PETPG*; *04PLUSPG*
|
||||
@ -4317,7 +4324,7 @@ filament_colour = #804040
|
||||
filament_max_volumetric_speed = 6
|
||||
first_layer_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]
|
||||
inherits = Kimya ABS Carbon; *ABSPG*; *04PLUSPG*
|
||||
@ -4577,7 +4584,7 @@ filament_type = PC
|
||||
filament_colour = #DEE0E6
|
||||
filament_max_volumetric_speed = 8
|
||||
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"
|
||||
|
||||
[filament:Prusament PC Blend @PG]
|
||||
@ -4609,12 +4616,12 @@ inherits = Prusament PC Blend
|
||||
first_layer_bed_temperature = 105
|
||||
bed_temperature = 110
|
||||
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]
|
||||
inherits = Prusament PC Blend
|
||||
filament_cost = 90.73
|
||||
filament_density = 1.16
|
||||
filament_density = 1.22
|
||||
extrusion_multiplier = 1.04
|
||||
first_layer_temperature = 285
|
||||
temperature = 285
|
||||
@ -4623,7 +4630,7 @@ fan_below_layer_time = 10
|
||||
filament_colour = #BBBBBB
|
||||
filament_retract_length = 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]
|
||||
inherits = Prusament PC Blend Carbon Fiber; *PCPG*
|
||||
@ -4667,7 +4674,7 @@ temperature = 285
|
||||
first_layer_bed_temperature = 90
|
||||
bed_temperature = 115
|
||||
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]
|
||||
inherits = Prusament PA11 Carbon Fiber; *PCPG*
|
||||
@ -4786,7 +4793,7 @@ inherits = *ABSC*
|
||||
filament_vendor = Generic
|
||||
filament_cost = 27.82
|
||||
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]
|
||||
inherits = Generic ABS; *ABSPG*
|
||||
@ -4923,7 +4930,7 @@ renamed_from = "Generic PET"
|
||||
filament_vendor = Generic
|
||||
filament_cost = 27.82
|
||||
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]
|
||||
inherits = Generic PETG; *PETPG*
|
||||
@ -5393,7 +5400,7 @@ filament_retract_lift = 0.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"
|
||||
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]
|
||||
inherits = addnorth Adura X; *PETPG*
|
||||
@ -5647,7 +5654,7 @@ filament_retract_length = 1.4
|
||||
filament_max_volumetric_speed = 5
|
||||
filament_spool_weight = 0
|
||||
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]
|
||||
inherits = addnorth Rigid X; *PETPG*; *04PLUSPG*
|
||||
@ -5693,7 +5700,7 @@ slowdown_below_layer_time = 15
|
||||
min_print_speed = 20
|
||||
filament_spool_weight = 0
|
||||
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]
|
||||
inherits = addnorth Textura; *PLAPG*
|
||||
@ -5730,7 +5737,7 @@ disable_fan_first_layers = 3
|
||||
fan_below_layer_time = 60
|
||||
slowdown_below_layer_time = 15
|
||||
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]
|
||||
inherits = Filamentworld ABS; *ABSPG*
|
||||
@ -5827,7 +5834,7 @@ filament_vendor = Filament PM
|
||||
filament_cost = 27.82
|
||||
filament_density = 1.27
|
||||
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]
|
||||
inherits = Filament PM PETG; *PETPG*
|
||||
@ -5843,7 +5850,7 @@ inherits = *PLA*
|
||||
filament_vendor = Generic
|
||||
filament_cost = 25.4
|
||||
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]
|
||||
inherits = Generic PLA; *PLAPG*
|
||||
@ -6092,6 +6099,220 @@ inherits = Spectrum PLA; *PLA06PG*
|
||||
[filament:Spectrum PLA @PG 0.8]
|
||||
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]
|
||||
inherits = *FLEX*
|
||||
filament_vendor = Generic
|
||||
@ -6596,7 +6817,7 @@ extrusion_multiplier = 0.95
|
||||
filament_density = 1.1
|
||||
first_layer_bed_temperature = 105
|
||||
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]
|
||||
inherits = Filatech FilaCarbon; *ABSPG*; *04PLUSPG*
|
||||
@ -6698,7 +6919,7 @@ first_layer_temperature = 230
|
||||
first_layer_bed_temperature = 100
|
||||
temperature = 225
|
||||
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]
|
||||
inherits = Filatech HIPS; *ABSPG*
|
||||
@ -6755,7 +6976,7 @@ cooling = 0
|
||||
bridge_fan_speed = 25
|
||||
filament_type = PA
|
||||
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]
|
||||
inherits = Filatech PA; *ABSPG*
|
||||
@ -7235,7 +7456,7 @@ min_fan_speed = 20
|
||||
max_fan_speed = 20
|
||||
bridge_fan_speed = 30
|
||||
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."
|
||||
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_density = 1.08
|
||||
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]
|
||||
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]
|
||||
inherits = Prusament PC Blend @MMU2
|
||||
filament_cost = 90.73
|
||||
filament_density = 1.16
|
||||
filament_density = 1.22
|
||||
extrusion_multiplier = 1.04
|
||||
fan_below_layer_time = 10
|
||||
first_layer_temperature = 280
|
||||
@ -7925,7 +8146,7 @@ max_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"
|
||||
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]
|
||||
inherits = *ABS*
|
||||
@ -7945,7 +8166,7 @@ max_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"
|
||||
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]
|
||||
inherits = Generic HIPS; *ABSPG*
|
||||
@ -7973,7 +8194,7 @@ filament_vendor = Made for Prusa
|
||||
filament_cost = 27.82
|
||||
filament_density = 1.27
|
||||
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]
|
||||
inherits = Prusa PETG; *PETPG*
|
||||
@ -8239,7 +8460,7 @@ filament_vendor = Made for Prusa
|
||||
filament_cost = 27.82
|
||||
filament_density = 1.24
|
||||
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]
|
||||
inherits = Prusa PLA; *PLAPG*
|
||||
@ -9350,7 +9571,7 @@ filament_cost = 36.29
|
||||
filament_density = 1.24
|
||||
filament_spool_weight = 201
|
||||
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]
|
||||
inherits = Prusament PLA; *PLAPG*
|
||||
@ -9377,7 +9598,7 @@ filament_max_volumetric_speed = 8
|
||||
filament_type = PVB
|
||||
filament_soluble = 1
|
||||
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
|
||||
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
|
||||
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"
|
||||
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]
|
||||
inherits = Taulman Bridge; *ABSPG*
|
||||
@ -9779,7 +10000,7 @@ temperature = 285
|
||||
first_layer_bed_temperature = 90
|
||||
bed_temperature = 90
|
||||
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
|
||||
min_fan_speed = 15
|
||||
filament_type = PA
|
||||
@ -10009,7 +10230,7 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no
|
||||
temperature = 235
|
||||
filament_wipe = 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]
|
||||
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]
|
||||
inherits = Prusament PC Blend @MINI
|
||||
filament_cost = 90.73
|
||||
filament_density = 1.16
|
||||
filament_density = 1.22
|
||||
extrusion_multiplier = 1.04
|
||||
first_layer_temperature = 280
|
||||
temperature = 280
|
||||
|
@ -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
|
||||
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
|
||||
|
@ -7,7 +7,7 @@
|
||||
name = Voron
|
||||
# Configuration version of this file. Config file will only be installed, if the config_version differs.
|
||||
# This means, the server may force the PrusaSlicer configuration to be downgraded.
|
||||
config_version = 1.0.1
|
||||
config_version = 1.0.3
|
||||
# Where to get the updates from?
|
||||
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
|
||||
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
|
||||
# not make it into the user interface
|
||||
|
||||
@ -183,7 +205,8 @@ deretract_speed = 25
|
||||
end_gcode = print_end ;end script from macro
|
||||
extruder_colour = #FFE3CA
|
||||
extruder_offset = 0x0
|
||||
gcode_flavor = marlin
|
||||
gcode_flavor = klipper
|
||||
autoemit_temperature_commands = 1
|
||||
layer_gcode = ;AFTER_LAYER_CHANGE\n;[layer_z]
|
||||
machine_max_acceleration_e = 10000
|
||||
machine_max_acceleration_extruding = 1500
|
||||
@ -309,6 +332,18 @@ max_print_height = 120
|
||||
printer_model = Voron_v0_120
|
||||
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]
|
||||
inherits = *Voron_v2_250*; *0.25nozzle*
|
||||
|
||||
@ -657,6 +692,89 @@ printer_variant = volcano 1.2
|
||||
printer_notes = Unoffical profile.\nPRINTER_HAS_BOWDEN\nVOLCANO
|
||||
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.
|
||||
# All other print presets will derive from the *common* print preset.
|
||||
@ -672,7 +790,8 @@ brim_width = 0
|
||||
clip_multipart_objects = 1
|
||||
compatible_printers =
|
||||
complete_objects = 0
|
||||
default_acceleration = 3000
|
||||
default_acceleration = 2000
|
||||
travel_acceleration = 3000
|
||||
dont_support_bridges = 1
|
||||
ensure_vertical_shell_thickness = 1
|
||||
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*
|
||||
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]
|
||||
inherits = *0.10mm*; *0.25nozzle*
|
||||
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*
|
||||
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]
|
||||
inherits = *0.15mm*; *0.25nozzle*
|
||||
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*
|
||||
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]
|
||||
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*
|
||||
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]
|
||||
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*
|
||||
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]
|
||||
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*
|
||||
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]
|
||||
inherits = *0.6mm*; *0.8nozzle*
|
||||
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*
|
||||
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]
|
||||
inherits = *0.8mm*; *1.2nozzle*
|
||||
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*
|
||||
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*]
|
||||
cooling = 1
|
||||
|
BIN
resources/profiles/Voron/Voron_SW_thumbnail.png
Normal file
BIN
resources/profiles/Voron/Voron_SW_thumbnail.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
BIN
resources/profiles/Voron/bedtexture-SW-250x210.png
Normal file
BIN
resources/profiles/Voron/bedtexture-SW-250x210.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 460 KiB |
BIN
resources/profiles/Voron/printbed-SW-MK52.stl
Normal file
BIN
resources/profiles/Voron/printbed-SW-MK52.stl
Normal file
Binary file not shown.
7
src/ankerl/README.txt
Normal file
7
src/ankerl/README.txt
Normal 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
1584
src/ankerl/unordered_dense.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -8,3 +8,5 @@ add_library(clipper STATIC
|
||||
clipper_z.cpp
|
||||
clipper_z.hpp
|
||||
)
|
||||
|
||||
target_link_libraries(clipper TBB::tbb TBB::tbbmalloc)
|
||||
|
@ -784,7 +784,7 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed)
|
||||
return false;
|
||||
|
||||
// Allocate a new edge array.
|
||||
std::vector<TEdge> edges(highI + 1);
|
||||
Edges edges(highI + 1);
|
||||
// Fill in the edge array.
|
||||
bool result = AddPathInternal(pg, highI, PolyTyp, Closed, edges.data());
|
||||
if (result)
|
||||
@ -1079,7 +1079,7 @@ Clipper::Clipper(int initOptions) :
|
||||
void Clipper::Reset()
|
||||
{
|
||||
ClipperBase::Reset();
|
||||
m_Scanbeam = std::priority_queue<cInt>();
|
||||
m_Scanbeam = std::priority_queue<cInt, cInts>{};
|
||||
m_Maxima.clear();
|
||||
m_ActiveEdges = 0;
|
||||
m_SortedEdges = 0;
|
||||
@ -2226,8 +2226,8 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge)
|
||||
if (!eLastHorz->NextInLML)
|
||||
eMaxPair = GetMaximaPair(eLastHorz);
|
||||
|
||||
std::vector<cInt>::const_iterator maxIt;
|
||||
std::vector<cInt>::const_reverse_iterator maxRit;
|
||||
cInts::const_iterator maxIt;
|
||||
cInts::const_reverse_iterator maxRit;
|
||||
if (!m_Maxima.empty())
|
||||
{
|
||||
//get the first maxima in range (X) ...
|
||||
@ -3941,7 +3941,7 @@ void CleanPolygon(const Path& in_poly, Path& out_poly, double distance)
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<OutPt> outPts(size);
|
||||
OutPts outPts(size);
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
outPts[i].Pt = in_poly[i];
|
||||
|
@ -39,6 +39,8 @@
|
||||
|
||||
#include <Eigen/Geometry>
|
||||
|
||||
#include <oneapi/tbb/scalable_allocator.h>
|
||||
|
||||
#define CLIPPER_VERSION "6.2.6"
|
||||
|
||||
//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;
|
||||
typedef std::vector<Path> Paths;
|
||||
template<typename BaseType>
|
||||
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 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};
|
||||
|
||||
class PolyNode;
|
||||
typedef std::vector< PolyNode* > PolyNodes;
|
||||
typedef std::vector<PolyNode*, Allocator<PolyNode*>> PolyNodes;
|
||||
|
||||
class PolyNode
|
||||
{
|
||||
@ -186,7 +191,7 @@ public:
|
||||
private:
|
||||
PolyTree(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
|
||||
};
|
||||
|
||||
@ -277,6 +282,8 @@ enum EdgeSide { esLeft = 1, esRight = 2};
|
||||
OutPt *Prev;
|
||||
};
|
||||
|
||||
using OutPts = std::vector<OutPt, Allocator<OutPt>>;
|
||||
|
||||
struct OutRec;
|
||||
struct Join {
|
||||
Join(OutPt *OutPt1, OutPt *OutPt2, IntPoint OffPt) :
|
||||
@ -312,7 +319,7 @@ public:
|
||||
if (num_paths == 1)
|
||||
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;
|
||||
size_t i = 0;
|
||||
for (const Path &pg : paths_provider) {
|
||||
@ -333,7 +340,7 @@ public:
|
||||
return false;
|
||||
|
||||
// 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.
|
||||
bool result = false;
|
||||
TEdge *p_edge = edges.data();
|
||||
@ -369,7 +376,7 @@ protected:
|
||||
void AscendToMax(TEdge *&E, bool Appending, bool IsClosed);
|
||||
|
||||
// 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
|
||||
static constexpr const bool m_UseFullRange = false;
|
||||
@ -380,7 +387,8 @@ protected:
|
||||
#endif // CLIPPERLIB_INT32
|
||||
|
||||
// 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.
|
||||
bool m_PreserveCollinear;
|
||||
// Is any of the paths inserted by AddPath() or AddPaths() open?
|
||||
@ -424,22 +432,23 @@ protected:
|
||||
private:
|
||||
|
||||
// Output polygons.
|
||||
std::vector<OutRec*> m_PolyOuts;
|
||||
std::vector<OutRec*, Allocator<OutRec*>> m_PolyOuts;
|
||||
// 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.
|
||||
OutPt *m_OutPtsFree;
|
||||
size_t m_OutPtsChunkSize;
|
||||
size_t m_OutPtsChunkLast;
|
||||
|
||||
std::vector<Join> m_Joins;
|
||||
std::vector<Join> m_GhostJoins;
|
||||
std::vector<IntersectNode> m_IntersectList;
|
||||
std::vector<Join, Allocator<Join>> m_Joins;
|
||||
std::vector<Join, Allocator<Join>> m_GhostJoins;
|
||||
std::vector<IntersectNode, Allocator<IntersectNode>> m_IntersectList;
|
||||
ClipType m_ClipType;
|
||||
// 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().
|
||||
std::vector<cInt> m_Maxima;
|
||||
cInts m_Maxima;
|
||||
TEdge *m_ActiveEdges;
|
||||
TEdge *m_SortedEdges;
|
||||
PolyFillType m_ClipFillType;
|
||||
@ -530,7 +539,7 @@ private:
|
||||
Paths m_destPolys;
|
||||
Path m_srcPoly;
|
||||
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_miterLim, m_StepsPerRad;
|
||||
// x: index of the lowest contour in m_polyNodes
|
||||
|
@ -24,5 +24,5 @@ set(LIBNEST2D_SRCFILES
|
||||
add_library(libnest2d STATIC ${LIBNEST2D_SRCFILES})
|
||||
|
||||
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)
|
||||
|
@ -339,15 +339,15 @@ public:
|
||||
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);
|
||||
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
|
||||
|
@ -206,7 +206,7 @@ std::vector<WaveSeed> wave_seeds(
|
||||
{
|
||||
assert(tiny_expansion > 0);
|
||||
|
||||
if (src.empty())
|
||||
if (src.empty() || boundary.empty())
|
||||
return {};
|
||||
|
||||
using Intersection = ClipperZUtils::ClipperZIntersectionVisitor::Intersection;
|
||||
|
@ -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);
|
||||
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));
|
||||
|
||||
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);
|
||||
}
|
||||
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));
|
||||
}
|
||||
|
||||
//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));
|
||||
return ret;
|
||||
}
|
||||
|
@ -5,7 +5,6 @@
|
||||
|
||||
#include <stack>
|
||||
#include <functional>
|
||||
#include <unordered_set>
|
||||
#include <sstream>
|
||||
#include <queue>
|
||||
#include <functional>
|
||||
@ -181,7 +180,7 @@ void SkeletalTrapezoidation::transferEdge(Point from, Point to, vd_t::edge_type&
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<Point> discretized = discretize(vd_edge, segments);
|
||||
Points discretized = discretize(vd_edge, segments);
|
||||
assert(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
|
||||
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();
|
||||
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.
|
||||
{
|
||||
@ -311,7 +310,7 @@ std::vector<Point> SkeletalTrapezoidation::discretize(const vd_t::edge_type& vd_
|
||||
//Start generating points along the edge.
|
||||
Point a = start;
|
||||
Point b = end;
|
||||
std::vector<Point> ret;
|
||||
Points ret;
|
||||
ret.emplace_back(a);
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
inline static void rotate_back_skeletal_trapezoidation_graph_after_fix(SkeletalTrapezoidationGraph &graph,
|
||||
const double fix_angle,
|
||||
const std::unordered_map<Point, Point, PointHash> &vertex_mapping)
|
||||
using PointMap = SkeletalTrapezoidation::PointMap;
|
||||
|
||||
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) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
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,
|
||||
const Polygons &polys,
|
||||
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;
|
||||
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) {
|
||||
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};
|
||||
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.
|
||||
Polygons polys_copy = polys;
|
||||
if (status != VoronoiDiagramStatus::NO_ISSUE_DETECTED) {
|
||||
@ -813,9 +814,11 @@ process_voronoi_diagram:
|
||||
edge.from->incident_edge = &edge;
|
||||
}
|
||||
|
||||
using NodeSet = SkeletalTrapezoidation::NodeSet;
|
||||
|
||||
void SkeletalTrapezoidation::separatePointyQuadEndNodes()
|
||||
{
|
||||
std::unordered_set<node_t*> visited_nodes;
|
||||
NodeSet visited_nodes;
|
||||
for (edge_t& edge : graph.edges)
|
||||
{
|
||||
if (edge.prev)
|
||||
@ -2285,16 +2288,18 @@ void SkeletalTrapezoidation::addToolpathSegment(const ExtrusionJunction& from, c
|
||||
|
||||
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)
|
||||
{
|
||||
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())
|
||||
{
|
||||
|
@ -7,8 +7,10 @@
|
||||
#include <boost/polygon/voronoi.hpp>
|
||||
|
||||
#include <memory> // smart pointers
|
||||
#include <unordered_map>
|
||||
#include <utility> // pair
|
||||
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include <Arachne/utils/VoronoiUtils.hpp>
|
||||
|
||||
#include "utils/HalfEdgeGraph.hpp"
|
||||
@ -80,7 +82,9 @@ class SkeletalTrapezoidation
|
||||
const BeadingStrategy& beading_strategy;
|
||||
|
||||
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.
|
||||
@ -164,8 +168,8 @@ protected:
|
||||
* 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
|
||||
*/
|
||||
std::unordered_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::edge_type*, edge_t*> vd_edge_to_he_edge;
|
||||
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.
|
||||
|
||||
/*!
|
||||
@ -204,7 +208,7 @@ protected:
|
||||
* \return A number of coordinates along the edge where the edge is broken
|
||||
* 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
|
||||
|
@ -2,7 +2,8 @@
|
||||
//CuraEngine is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
#include "SkeletalTrapezoidationGraph.hpp"
|
||||
#include <unordered_map>
|
||||
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
@ -180,8 +181,8 @@ bool STHalfEdgeNode::isLocalMaximum(bool strict) const
|
||||
|
||||
void SkeletalTrapezoidationGraph::collapseSmallEdges(coord_t snap_dist)
|
||||
{
|
||||
std::unordered_map<edge_t*, std::list<edge_t>::iterator> edge_locator;
|
||||
std::unordered_map<node_t*, std::list<node_t>::iterator> node_locator;
|
||||
ankerl::unordered_dense::map<edge_t*, Edges::iterator> edge_locator;
|
||||
ankerl::unordered_dense::map<node_t*, Nodes::iterator> node_locator;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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()
|
||||
&& to_be_removed == &*current_edge_it)
|
||||
|
@ -2,7 +2,6 @@
|
||||
// CuraEngine is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
#include <algorithm> //For std::partition_copy and std::min_element.
|
||||
#include <unordered_set>
|
||||
|
||||
#include "WallToolPaths.hpp"
|
||||
|
||||
@ -341,7 +340,7 @@ void removeSmallAreas(Polygons &thiss, const double min_area_size, const bool re
|
||||
}
|
||||
} else {
|
||||
// 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;) {
|
||||
if (double area = ClipperLib::Area(to_path(*it)); fabs(area) < min_area_size) {
|
||||
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.
|
||||
*/
|
||||
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,
|
||||
// so that we can easily find which two toolpaths are next to each other,
|
||||
|
@ -5,7 +5,8 @@
|
||||
#define CURAENGINE_WALLTOOLPATHS_H
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include "BeadingStrategy/BeadingStrategyFactory.hpp"
|
||||
#include "utils/ExtrusionLine.hpp"
|
||||
@ -73,6 +74,7 @@ public:
|
||||
*/
|
||||
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.
|
||||
* 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.
|
||||
*/
|
||||
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:
|
||||
/*!
|
||||
|
@ -21,8 +21,10 @@ class HalfEdgeGraph
|
||||
public:
|
||||
using edge_t = derived_edge_t;
|
||||
using node_t = derived_node_t;
|
||||
std::list<edge_t> edges;
|
||||
std::list<node_t> nodes;
|
||||
using Edges = std::list<edge_t>;
|
||||
using Nodes = std::list<node_t>;
|
||||
Edges edges;
|
||||
Nodes nodes;
|
||||
};
|
||||
|
||||
} // namespace Slic3r::Arachne
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include "SparsePointGrid.hpp"
|
||||
#include "PolygonsPointIndex.hpp"
|
||||
#include "../../Polygon.hpp"
|
||||
#include <unordered_set>
|
||||
#include <cassert>
|
||||
|
||||
namespace Slic3r::Arachne
|
||||
|
@ -6,7 +6,6 @@
|
||||
#define UTILS_SPARSE_GRID_H
|
||||
|
||||
#include <cassert>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
|
@ -6,7 +6,6 @@
|
||||
#define UTILS_SPARSE_LINE_GRID_H
|
||||
|
||||
#include <cassert>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
|
@ -6,7 +6,6 @@
|
||||
#define UTILS_SPARSE_POINT_GRID_H
|
||||
|
||||
#include <cassert>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "SparseGrid.hpp"
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include "../../Point.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
|
@ -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]));
|
||||
}
|
||||
};
|
||||
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
|
||||
// xx is point projected on the segment ab
|
||||
const Point a = segment.from();
|
||||
|
@ -34,7 +34,7 @@ public:
|
||||
* 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.
|
||||
*/
|
||||
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)
|
||||
{
|
||||
|
@ -6,23 +6,19 @@
|
||||
|
||||
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 BoundingBox3Base<Vec3d>::BoundingBox3Base(const std::vector<Vec3d> &points);
|
||||
|
||||
void BoundingBox::polygon(Polygon* polygon) const
|
||||
{
|
||||
polygon->points.clear();
|
||||
polygon->points.resize(4);
|
||||
polygon->points[0](0) = this->min(0);
|
||||
polygon->points[0](1) = this->min(1);
|
||||
polygon->points[1](0) = this->max(0);
|
||||
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->points = {
|
||||
this->min,
|
||||
{ this->max.x(), this->min.y() },
|
||||
this->max,
|
||||
{ this->min.x(), this->max.y() }
|
||||
};
|
||||
}
|
||||
|
||||
Polygon BoundingBox::polygon() const
|
||||
@ -37,8 +33,8 @@ BoundingBox BoundingBox::rotated(double angle) const
|
||||
BoundingBox out;
|
||||
out.merge(this->min.rotated(angle));
|
||||
out.merge(this->max.rotated(angle));
|
||||
out.merge(Point(this->min(0), this->max(1)).rotated(angle));
|
||||
out.merge(Point(this->max(0), this->min(1)).rotated(angle));
|
||||
out.merge(Point(this->min.x(), this->max.y()).rotated(angle));
|
||||
out.merge(Point(this->max.x(), this->min.y()).rotated(angle));
|
||||
return out;
|
||||
}
|
||||
|
||||
@ -47,23 +43,23 @@ BoundingBox BoundingBox::rotated(double angle, const Point ¢er) const
|
||||
BoundingBox out;
|
||||
out.merge(this->min.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->max(0), this->min(1)).rotated(angle, center));
|
||||
out.merge(Point(this->min.x(), this->max.y()).rotated(angle, center));
|
||||
out.merge(Point(this->max.x(), this->min.y()).rotated(angle, center));
|
||||
return out;
|
||||
}
|
||||
|
||||
template <class PointClass> void
|
||||
BoundingBoxBase<PointClass>::scale(double factor)
|
||||
template <class PointType, typename APointsType> void
|
||||
BoundingBoxBase<PointType, APointsType>::scale(double factor)
|
||||
{
|
||||
this->min *= 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<Vec3d>::scale(double factor);
|
||||
|
||||
template <class PointClass> void
|
||||
BoundingBoxBase<PointClass>::merge(const PointClass &point)
|
||||
template <class PointType, typename APointsType> void
|
||||
BoundingBoxBase<PointType, APointsType>::merge(const PointType &point)
|
||||
{
|
||||
if (this->defined) {
|
||||
this->min = this->min.cwiseMin(point);
|
||||
@ -74,22 +70,22 @@ BoundingBoxBase<PointClass>::merge(const PointClass &point)
|
||||
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<Vec2d>::merge(const Vec2d &point);
|
||||
|
||||
template <class PointClass> void
|
||||
BoundingBoxBase<PointClass>::merge(const std::vector<PointClass> &points)
|
||||
template <class PointType, typename APointsType> void
|
||||
BoundingBoxBase<PointType, APointsType>::merge(const PointsType &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 <class PointClass> void
|
||||
BoundingBoxBase<PointClass>::merge(const BoundingBoxBase<PointClass> &bb)
|
||||
template <class PointType, typename APointsType> void
|
||||
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 (this->defined) {
|
||||
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<Vec2d>::merge(const BoundingBoxBase<Vec2d> &bb);
|
||||
|
||||
template <class PointClass> void
|
||||
BoundingBox3Base<PointClass>::merge(const PointClass &point)
|
||||
template <class PointType> void
|
||||
BoundingBox3Base<PointType>::merge(const PointType &point)
|
||||
{
|
||||
if (this->defined) {
|
||||
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<Vec3d>::merge(const Vec3d &point);
|
||||
|
||||
template <class PointClass> void
|
||||
BoundingBox3Base<PointClass>::merge(const std::vector<PointClass> &points)
|
||||
template <class PointType> void
|
||||
BoundingBox3Base<PointType>::merge(const PointsType &points)
|
||||
{
|
||||
this->merge(BoundingBox3Base(points));
|
||||
}
|
||||
template void BoundingBox3Base<Vec3d>::merge(const Pointf3s &points);
|
||||
|
||||
template <class PointClass> void
|
||||
BoundingBox3Base<PointClass>::merge(const BoundingBox3Base<PointClass> &bb)
|
||||
template <class PointType> void
|
||||
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 (this->defined) {
|
||||
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 <class PointClass> PointClass
|
||||
BoundingBoxBase<PointClass>::size() const
|
||||
template <class PointType, typename APointsType> PointType
|
||||
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 Vec2d BoundingBoxBase<Vec2d>::size() const;
|
||||
|
||||
template <class PointClass> PointClass
|
||||
BoundingBox3Base<PointClass>::size() const
|
||||
template <class PointType> PointType
|
||||
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 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);
|
||||
double x = this->max(0) - this->min(0);
|
||||
double y = this->max(1) - this->min(1);
|
||||
return 0.5 * sqrt(x*x+y*y);
|
||||
return 0.5 * (this->max - this->min).template cast<double>().norm();
|
||||
}
|
||||
template double BoundingBoxBase<Point>::radius() const;
|
||||
template double BoundingBoxBase<Point, Points>::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);
|
||||
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);
|
||||
return 0.5 * (this->max - this->min).template cast<double>().norm();
|
||||
}
|
||||
template double BoundingBox3Base<Vec3d>::radius() const;
|
||||
|
||||
template <class PointClass> void
|
||||
BoundingBoxBase<PointClass>::offset(coordf_t delta)
|
||||
template <class PointType, typename APointsType> void
|
||||
BoundingBoxBase<PointType, APointsType>::offset(coordf_t delta)
|
||||
{
|
||||
PointClass v(delta, delta);
|
||||
PointType v(delta, delta);
|
||||
this->min -= 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 <class PointClass> void
|
||||
BoundingBox3Base<PointClass>::offset(coordf_t delta)
|
||||
template <class PointType> void
|
||||
BoundingBox3Base<PointType>::offset(coordf_t delta)
|
||||
{
|
||||
PointClass v(delta, delta, delta);
|
||||
PointType v(delta, delta, delta);
|
||||
this->min -= v;
|
||||
this->max += v;
|
||||
}
|
||||
template void BoundingBox3Base<Vec3d>::offset(coordf_t delta);
|
||||
|
||||
template <class PointClass> PointClass
|
||||
BoundingBoxBase<PointClass>::center() const
|
||||
template <class PointType, typename APointsType> PointType
|
||||
BoundingBoxBase<PointType, APointsType>::center() const
|
||||
{
|
||||
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 Vec2d BoundingBoxBase<Vec2d>::center() const;
|
||||
|
||||
template <class PointClass> PointClass
|
||||
BoundingBox3Base<PointClass>::center() const
|
||||
template <class PointType> PointType
|
||||
BoundingBox3Base<PointType>::center() const
|
||||
{
|
||||
return (this->min + this->max) / 2;
|
||||
}
|
||||
template Vec3f BoundingBox3Base<Vec3f>::center() const;
|
||||
template Vec3d BoundingBox3Base<Vec3d>::center() const;
|
||||
|
||||
template <class PointClass> coordf_t
|
||||
BoundingBox3Base<PointClass>::max_size() const
|
||||
template <class PointType> coordf_t
|
||||
BoundingBox3Base<PointType>::max_size() const
|
||||
{
|
||||
PointClass s = size();
|
||||
return std::max(s(0), std::max(s(1), s(2)));
|
||||
PointType s = size();
|
||||
return std::max(s.x(), std::max(s.y(), s.z()));
|
||||
}
|
||||
template coordf_t BoundingBox3Base<Vec3f>::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)
|
||||
{
|
||||
if (this->defined) {
|
||||
min(0) = Slic3r::align_to_grid(min(0), cell_size);
|
||||
min(1) = Slic3r::align_to_grid(min(1), cell_size);
|
||||
min.x() = Slic3r::align_to_grid(min.x(), 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;
|
||||
|
||||
Vertices src_vertices;
|
||||
src_vertices(0, 0) = min(0); src_vertices(1, 0) = min(1); src_vertices(2, 0) = min(2);
|
||||
src_vertices(0, 1) = max(0); src_vertices(1, 1) = min(1); src_vertices(2, 1) = min(2);
|
||||
src_vertices(0, 2) = max(0); src_vertices(1, 2) = max(1); src_vertices(2, 2) = min(2);
|
||||
src_vertices(0, 3) = min(0); src_vertices(1, 3) = max(1); src_vertices(2, 3) = min(2);
|
||||
src_vertices(0, 4) = min(0); src_vertices(1, 4) = min(1); src_vertices(2, 4) = max(2);
|
||||
src_vertices(0, 5) = max(0); src_vertices(1, 5) = min(1); src_vertices(2, 5) = max(2);
|
||||
src_vertices(0, 6) = max(0); src_vertices(1, 6) = max(1); src_vertices(2, 6) = max(2);
|
||||
src_vertices(0, 7) = min(0); src_vertices(1, 7) = max(1); src_vertices(2, 7) = max(2);
|
||||
src_vertices(0, 0) = min.x(); src_vertices(1, 0) = min.y(); src_vertices(2, 0) = min.z();
|
||||
src_vertices(0, 1) = max.x(); src_vertices(1, 1) = min.y(); src_vertices(2, 1) = min.z();
|
||||
src_vertices(0, 2) = max.x(); src_vertices(1, 2) = max.y(); src_vertices(2, 2) = min.z();
|
||||
src_vertices(0, 3) = min.x(); src_vertices(1, 3) = max.y(); src_vertices(2, 3) = min.z();
|
||||
src_vertices(0, 4) = min.x(); src_vertices(1, 4) = min.y(); src_vertices(2, 4) = max.z();
|
||||
src_vertices(0, 5) = max.x(); src_vertices(1, 5) = min.y(); src_vertices(2, 5) = max.z();
|
||||
src_vertices(0, 6) = max.x(); src_vertices(1, 6) = max.y(); src_vertices(2, 6) = max.z();
|
||||
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();
|
||||
|
||||
|
@ -8,53 +8,54 @@
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
template <class PointClass>
|
||||
template <typename PointType, typename APointsType = std::vector<PointType>>
|
||||
class BoundingBoxBase
|
||||
{
|
||||
public:
|
||||
PointClass min;
|
||||
PointClass max;
|
||||
using PointsType = APointsType;
|
||||
PointType min;
|
||||
PointType max;
|
||||
bool defined;
|
||||
|
||||
BoundingBoxBase() : min(PointClass::Zero()), max(PointClass::Zero()), defined(false) {}
|
||||
BoundingBoxBase(const PointClass &pmin, const PointClass &pmax) :
|
||||
BoundingBoxBase() : min(PointType::Zero()), max(PointType::Zero()), defined(false) {}
|
||||
BoundingBoxBase(const PointType &pmin, const PointType &pmax) :
|
||||
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); }
|
||||
|
||||
template<class It, class = IteratorOnly<It>>
|
||||
BoundingBoxBase(It from, It to)
|
||||
{ construct(*this, from, to); }
|
||||
|
||||
BoundingBoxBase(const std::vector<PointClass> &points)
|
||||
BoundingBoxBase(const PointsType &points)
|
||||
: BoundingBoxBase(points.begin(), points.end())
|
||||
{}
|
||||
|
||||
void reset() { this->defined = false; this->min = PointClass::Zero(); this->max = PointClass::Zero(); }
|
||||
void merge(const PointClass &point);
|
||||
void merge(const std::vector<PointClass> &points);
|
||||
void merge(const BoundingBoxBase<PointClass> &bb);
|
||||
void reset() { this->defined = false; this->min = PointType::Zero(); this->max = PointType::Zero(); }
|
||||
void merge(const PointType &point);
|
||||
void merge(const PointsType &points);
|
||||
void merge(const BoundingBoxBase<PointType, PointsType> &bb);
|
||||
void scale(double factor);
|
||||
PointClass size() const;
|
||||
PointType size() 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(const PointClass &v) { 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 PointType &v) { this->min += v; this->max += v; }
|
||||
void offset(coordf_t delta);
|
||||
BoundingBoxBase<PointClass> inflated(coordf_t delta) const throw() { BoundingBoxBase<PointClass> out(*this); out.offset(delta); return out; }
|
||||
PointClass center() const;
|
||||
bool contains(const PointClass &point) const {
|
||||
BoundingBoxBase<PointType, PointsType> inflated(coordf_t delta) const throw() { BoundingBoxBase<PointType, PointsType> out(*this); out.offset(delta); return out; }
|
||||
PointType center() const;
|
||||
bool contains(const PointType &point) const {
|
||||
return point.x() >= this->min.x() && point.x() <= this->max.x()
|
||||
&& 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);
|
||||
}
|
||||
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() ||
|
||||
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<PointClass> &rhs) { return ! (*this == rhs); }
|
||||
bool operator==(const BoundingBoxBase<PointType, PointsType> &rhs) { return this->min == rhs.min && this->max == rhs.max; }
|
||||
bool operator!=(const BoundingBoxBase<PointType, PointsType> &rhs) { return ! (*this == rhs); }
|
||||
|
||||
private:
|
||||
// to access construct()
|
||||
@ -69,10 +70,10 @@ private:
|
||||
{
|
||||
if (from != to) {
|
||||
auto it = from;
|
||||
out.min = it->template cast<typename PointClass::Scalar>();
|
||||
out.min = it->template cast<typename PointType::Scalar>();
|
||||
out.max = out.min;
|
||||
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.max = out.max.cwiseMax(vec);
|
||||
}
|
||||
@ -81,16 +82,18 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
template <class PointClass>
|
||||
class BoundingBox3Base : public BoundingBoxBase<PointClass>
|
||||
template <class PointType>
|
||||
class BoundingBox3Base : public BoundingBoxBase<PointType, std::vector<PointType>>
|
||||
{
|
||||
public:
|
||||
BoundingBox3Base() : BoundingBoxBase<PointClass>() {}
|
||||
BoundingBox3Base(const PointClass &pmin, const PointClass &pmax) :
|
||||
BoundingBoxBase<PointClass>(pmin, pmax)
|
||||
{ if (pmin.z() >= pmax.z()) BoundingBoxBase<PointClass>::defined = false; }
|
||||
BoundingBox3Base(const PointClass &p1, const PointClass &p2, const PointClass &p3) :
|
||||
BoundingBoxBase<PointClass>(p1, p1) { merge(p2); merge(p3); }
|
||||
using PointsType = std::vector<PointType>;
|
||||
|
||||
BoundingBox3Base() : BoundingBoxBase<PointType>() {}
|
||||
BoundingBox3Base(const PointType &pmin, const PointType &pmax) :
|
||||
BoundingBoxBase<PointType>(pmin, pmax)
|
||||
{ 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)
|
||||
{
|
||||
@ -98,67 +101,67 @@ public:
|
||||
throw Slic3r::InvalidArgument("Empty point set supplied to BoundingBox3Base constructor");
|
||||
|
||||
auto it = from;
|
||||
this->min = it->template cast<typename PointClass::Scalar>();
|
||||
this->min = it->template cast<typename PointType::Scalar>();
|
||||
this->max = this->min;
|
||||
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->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());
|
||||
}
|
||||
|
||||
BoundingBox3Base(const std::vector<PointClass> &points)
|
||||
BoundingBox3Base(const PointsType &points)
|
||||
: BoundingBox3Base(points.begin(), points.end())
|
||||
{}
|
||||
|
||||
void merge(const PointClass &point);
|
||||
void merge(const std::vector<PointClass> &points);
|
||||
void merge(const BoundingBox3Base<PointClass> &bb);
|
||||
PointClass size() const;
|
||||
void merge(const PointType &point);
|
||||
void merge(const PointsType &points);
|
||||
void merge(const BoundingBox3Base<PointType> &bb);
|
||||
PointType size() 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 offset(coordf_t delta);
|
||||
BoundingBox3Base<PointClass> inflated(coordf_t delta) const throw() { BoundingBox3Base<PointClass> out(*this); out.offset(delta); return out; }
|
||||
PointClass center() const;
|
||||
BoundingBox3Base<PointType> inflated(coordf_t delta) const throw() { BoundingBox3Base<PointType> out(*this); out.offset(delta); return out; }
|
||||
PointType center() const;
|
||||
coordf_t max_size() const;
|
||||
|
||||
bool contains(const PointClass &point) const {
|
||||
return BoundingBoxBase<PointClass>::contains(point) && point.z() >= this->min.z() && point.z() <= this->max.z();
|
||||
bool contains(const PointType &point) const {
|
||||
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);
|
||||
}
|
||||
|
||||
// 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() &&
|
||||
this->min.z() < other.max.z() && this->max.z() > other.min.z();
|
||||
}
|
||||
};
|
||||
|
||||
// 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<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<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<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<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<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 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 Point BoundingBoxBase<Point>::center() const;
|
||||
extern template Point BoundingBoxBase<Point, Points>::center() const;
|
||||
extern template Vec2f BoundingBoxBase<Vec2f>::center() const;
|
||||
extern template Vec2d BoundingBoxBase<Vec2d>::center() const;
|
||||
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<Vec3d>::max_size() const;
|
||||
|
||||
class BoundingBox : public BoundingBoxBase<Point>
|
||||
class BoundingBox : public BoundingBoxBase<Point, Points>
|
||||
{
|
||||
public:
|
||||
void polygon(Polygon* polygon) const;
|
||||
@ -187,9 +190,9 @@ public:
|
||||
// to encompass the original bounding box.
|
||||
void align_to_grid(const coord_t cell_size);
|
||||
|
||||
BoundingBox() : BoundingBoxBase<Point>() {}
|
||||
BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase<Point>(pmin, pmax) {}
|
||||
BoundingBox(const Points &points) : BoundingBoxBase<Point>(points) {}
|
||||
BoundingBox() : BoundingBoxBase<Point, Points>() {}
|
||||
BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase<Point, Points>(pmin, pmax) {}
|
||||
BoundingBox(const Points &points) : BoundingBoxBase<Point, Points>(points) {}
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
template<typename VT>
|
||||
inline bool empty(const BoundingBoxBase<VT> &bb)
|
||||
template<typename PointType, typename PointsType>
|
||||
inline bool empty(const BoundingBoxBase<PointType, PointsType> &bb)
|
||||
{
|
||||
return ! bb.defined || bb.min.x() >= bb.max.x() || bb.min.y() >= bb.max.y();
|
||||
}
|
||||
|
||||
template<typename VT>
|
||||
inline bool empty(const BoundingBox3Base<VT> &bb)
|
||||
template<typename PointType>
|
||||
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();
|
||||
}
|
||||
|
@ -127,8 +127,8 @@ set(SLIC3R_SOURCES
|
||||
Format/SL1.cpp
|
||||
Format/SL1_SVG.hpp
|
||||
Format/SL1_SVG.cpp
|
||||
Format/pwmx.hpp
|
||||
Format/pwmx.cpp
|
||||
Format/AnycubicSLA.hpp
|
||||
Format/AnycubicSLA.cpp
|
||||
Format/STEP.hpp
|
||||
Format/STEP.cpp
|
||||
GCode/ThumbnailData.cpp
|
||||
@ -485,6 +485,7 @@ target_link_libraries(libslic3r
|
||||
qhull
|
||||
semver
|
||||
TBB::tbb
|
||||
TBB::tbbmalloc
|
||||
libslic3r_cgal
|
||||
${CMAKE_DL_LIBS}
|
||||
PNG::PNG
|
||||
|
@ -1,6 +1,21 @@
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "Geometry.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
|
||||
|
||||
@ -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.
|
||||
// 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.
|
||||
template<typename PointType>
|
||||
inline void clip_clipper_polygon_with_subject_bbox_templ(const std::vector<PointType> &src, const BoundingBox &bbox, std::vector<PointType> &out)
|
||||
template<typename PointsType>
|
||||
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();
|
||||
const size_t cnt = src.size();
|
||||
if (cnt < 3)
|
||||
@ -107,10 +124,10 @@ namespace ClipperUtils {
|
||||
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); }
|
||||
|
||||
template<typename PointType>
|
||||
[[nodiscard]] std::vector<PointType> clip_clipper_polygon_with_subject_bbox_templ(const std::vector<PointType> &src, const BoundingBox &bbox)
|
||||
template<typename PointsType>
|
||||
[[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);
|
||||
return out;
|
||||
}
|
||||
@ -257,6 +274,8 @@ bool has_duplicate_points(const ClipperLib::PolyTree &polytree)
|
||||
template<typename PathsProvider, ClipperLib::EndType endType = ClipperLib::etClosedPolygon>
|
||||
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::Paths out;
|
||||
out.reserve(paths.size());
|
||||
@ -298,6 +317,8 @@ TResult clipper_do(
|
||||
TClip && clip,
|
||||
const ClipperLib::PolyFillType fillType)
|
||||
{
|
||||
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
|
||||
|
||||
ClipperLib::Clipper clipper;
|
||||
clipper.AddPaths(std::forward<TSubj>(subject), ClipperLib::ptSubject, 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
|
||||
const ClipperLib::PolyFillType fillType = ClipperLib::pftNonZero)
|
||||
{
|
||||
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
|
||||
|
||||
ClipperLib::Clipper clipper;
|
||||
clipper.AddPaths(std::forward<TSubj>(subject), ClipperLib::ptSubject, true);
|
||||
TResult retval;
|
||||
@ -365,6 +388,8 @@ template<> void remove_outermost_polygon<ClipperLib::PolyTree>(ClipperLib::PolyT
|
||||
template<class TResult, typename PathsProvider>
|
||||
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);
|
||||
TResult out;
|
||||
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).
|
||||
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.
|
||||
ClipperLib::Paths contours;
|
||||
{
|
||||
@ -612,6 +639,8 @@ inline ClipperLib::PolyTree clipper_do_polytree(
|
||||
PathProvider2 &&clip,
|
||||
const ClipperLib::PolyFillType fillType)
|
||||
{
|
||||
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
|
||||
|
||||
// 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
|
||||
// if there are overapping edges.
|
||||
@ -750,6 +779,8 @@ Slic3r::ExPolygons union_ex(const Slic3r::Surfaces &subject)
|
||||
template<typename PathsProvider1, typename PathsProvider2>
|
||||
Polylines _clipper_pl_open(ClipperLib::ClipType clipType, PathsProvider1 &&subject, PathsProvider2 &&clip)
|
||||
{
|
||||
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
|
||||
|
||||
ClipperLib::Clipper clipper;
|
||||
clipper.AddPaths(std::forward<PathsProvider1>(subject), ClipperLib::ptSubject, false);
|
||||
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)
|
||||
{
|
||||
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
|
||||
|
||||
ClipperLib::Paths output;
|
||||
if (preserve_collinear) {
|
||||
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)
|
||||
{
|
||||
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
|
||||
|
||||
if (! preserve_collinear)
|
||||
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)
|
||||
{
|
||||
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
|
||||
|
||||
// init Clipper
|
||||
ClipperLib::Clipper clipper;
|
||||
clipper.Clear();
|
||||
@ -991,6 +1028,8 @@ ClipperLib::Paths fix_after_outer_offset(
|
||||
ClipperLib::PolyFillType filltype, // = ClipperLib::pftPositive
|
||||
bool reverse_result) // = false
|
||||
{
|
||||
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
|
||||
|
||||
ClipperLib::Paths solution;
|
||||
if (! input.empty()) {
|
||||
ClipperLib::Clipper clipper;
|
||||
@ -1009,6 +1048,8 @@ ClipperLib::Paths fix_after_inner_offset(
|
||||
ClipperLib::PolyFillType filltype, // = ClipperLib::pftNegative
|
||||
bool reverse_result) // = true
|
||||
{
|
||||
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
|
||||
|
||||
ClipperLib::Paths solution;
|
||||
if (! input.empty()) {
|
||||
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)
|
||||
{
|
||||
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
|
||||
|
||||
assert(contour.size() == deltas.size());
|
||||
|
||||
#ifndef NDEBUG
|
||||
@ -1167,34 +1210,49 @@ ClipperLib::Path mittered_offset_path_scaled(const Points &contour, const std::v
|
||||
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)
|
||||
{
|
||||
#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());
|
||||
#endif /* NDEBUG */
|
||||
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
|
||||
|
||||
// 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 */
|
||||
ClipperLib::Paths contours, holes;
|
||||
variable_offset_inner_raw(expoly, deltas, miter_limit, contours, holes);
|
||||
|
||||
// 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.
|
||||
// Subtract holes from the contours.
|
||||
ClipperLib::Paths output;
|
||||
if (holes.empty())
|
||||
output = std::move(contours);
|
||||
@ -1202,6 +1260,8 @@ Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector<std::v
|
||||
ClipperLib::Clipper clipper;
|
||||
clipper.Clear();
|
||||
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.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));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
#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());
|
||||
#endif /* NDEBUG */
|
||||
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
|
||||
|
||||
// 1) Offset the outer contour.
|
||||
ClipperLib::Paths contours = fix_after_outer_offset(mittered_offset_path_scaled(expoly.contour.points, deltas.front(), miter_limit), ClipperLib::pftPositive, false);
|
||||
#ifndef NDEBUG
|
||||
for (auto &c : contours)
|
||||
assert(ClipperLib::Area(c) > 0.);
|
||||
#endif /* NDEBUG */
|
||||
ClipperLib::Paths contours, holes;
|
||||
variable_offset_outer_raw(expoly, deltas, miter_limit, contours, holes);
|
||||
|
||||
// 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_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 */
|
||||
// Subtract holes from the contours.
|
||||
ClipperLib::Paths output;
|
||||
if (holes.empty())
|
||||
output = std::move(contours);
|
||||
else {
|
||||
//FIXME the difference is not needed as the holes may never intersect with other holes.
|
||||
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);
|
||||
}
|
||||
|
||||
// 3) Subtract holes from the contours.
|
||||
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));
|
||||
return to_polygons(std::move(output));
|
||||
}
|
||||
|
||||
ExPolygons variable_offset_outer_ex(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit)
|
||||
{
|
||||
#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());
|
||||
#endif /* NDEBUG */
|
||||
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
|
||||
|
||||
// 1) Offset the outer contour.
|
||||
ClipperLib::Paths contours = fix_after_outer_offset(mittered_offset_path_scaled(expoly.contour.points, deltas.front(), miter_limit), ClipperLib::pftPositive, false);
|
||||
#ifndef NDEBUG
|
||||
for (auto &c : contours)
|
||||
assert(ClipperLib::Area(c) > 0.);
|
||||
#endif /* NDEBUG */
|
||||
ClipperLib::Paths contours, holes;
|
||||
variable_offset_outer_raw(expoly, deltas, miter_limit, contours, holes);
|
||||
|
||||
// 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_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.
|
||||
// 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 {
|
||||
ClipperLib::Clipper clipper;
|
||||
clipper.AddPaths(contours, ClipperLib::ptSubject, true);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
ExPolygons variable_offset_inner_ex(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit)
|
||||
{
|
||||
#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());
|
||||
#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));
|
||||
output.reserve(1);
|
||||
if (contours.size() > 1) {
|
||||
// One expolygon with holes created by closing a C shape. Which is which?
|
||||
output.push_back({});
|
||||
ExPolygon &out = output.back();
|
||||
out.holes.reserve(contours.size() - 1);
|
||||
for (ClipperLib::Path &path : contours) {
|
||||
if (ClipperLib::Area(path) > 0) {
|
||||
// Only one contour with positive area is expected to be created by an outer offset of an ExPolygon.
|
||||
assert(out.contour.empty());
|
||||
out.contour.points = std::move(path);
|
||||
} else
|
||||
out.holes.push_back(Polygon{ std::move(path) });
|
||||
}
|
||||
} else {
|
||||
// Single contour must be CCW.
|
||||
assert(contours.size() == 1);
|
||||
assert(ClipperLib::Area(contours.front()) > 0);
|
||||
output.push_back(ExPolygon{ std::move(contours.front()) });
|
||||
}
|
||||
} else {
|
||||
//FIXME the difference is not needed as the holes may never intersect with other holes.
|
||||
ClipperLib::Clipper clipper;
|
||||
// Contours may have holes if they were created by closing a C shape.
|
||||
clipper.AddPaths(contours, ClipperLib::ptSubject, true);
|
||||
clipper.AddPaths(holes, ClipperLib::ptClip, true);
|
||||
ClipperLib::PolyTree polytree;
|
||||
@ -1339,6 +1398,7 @@ ExPolygons variable_offset_inner_ex(const ExPolygon &expoly, const std::vector<s
|
||||
output = PolyTreeToExPolygons(std::move(polytree));
|
||||
}
|
||||
|
||||
assert(output.size() == 1);
|
||||
return output;
|
||||
}
|
||||
|
||||
|
@ -133,21 +133,21 @@ namespace ClipperUtils {
|
||||
const std::vector<PathType> &m_paths;
|
||||
};
|
||||
|
||||
template<typename MultiPointType>
|
||||
template<typename MultiPointsType>
|
||||
class MultiPointsProvider {
|
||||
public:
|
||||
MultiPointsProvider(const std::vector<MultiPointType> &multipoints) : m_multipoints(multipoints) {}
|
||||
MultiPointsProvider(const MultiPointsType &multipoints) : m_multipoints(multipoints) {}
|
||||
|
||||
struct iterator : public PathsProviderIteratorBase {
|
||||
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; }
|
||||
bool operator==(const iterator &rhs) const { return m_it == rhs.m_it; }
|
||||
bool operator!=(const iterator &rhs) const { return !(*this == rhs); }
|
||||
const Points& operator++(int) { return (m_it ++)->points; }
|
||||
iterator& operator++() { ++ m_it; return *this; }
|
||||
private:
|
||||
typename std::vector<MultiPointType>::const_iterator m_it;
|
||||
typename MultiPointsType::const_iterator m_it;
|
||||
};
|
||||
|
||||
iterator cbegin() const { return iterator(m_multipoints.begin()); }
|
||||
@ -157,11 +157,11 @@ namespace ClipperUtils {
|
||||
size_t size() const { return m_multipoints.size(); }
|
||||
|
||||
private:
|
||||
const std::vector<MultiPointType> &m_multipoints;
|
||||
const MultiPointsType &m_multipoints;
|
||||
};
|
||||
|
||||
using PolygonsProvider = MultiPointsProvider<Polygon>;
|
||||
using PolylinesProvider = MultiPointsProvider<Polyline>;
|
||||
using PolygonsProvider = MultiPointsProvider<Polygons>;
|
||||
using PolylinesProvider = MultiPointsProvider<Polylines>;
|
||||
|
||||
struct ExPolygonProvider {
|
||||
ExPolygonProvider(const ExPolygon &expoly) : m_expoly(expoly) {}
|
||||
|
@ -40,7 +40,7 @@ inline ZPath to_zpath(const Points &path, coord_t z)
|
||||
// Convert multiple paths to paths with a given Z coordinate.
|
||||
// If Open, then duplicate the first point of each path at its end.
|
||||
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;
|
||||
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.
|
||||
// If Open, then duplicate the first point of each path at its end.
|
||||
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());
|
||||
for (const ZPoints &path : paths)
|
||||
out.emplace_back(from_zpath<Open>(path));
|
||||
}
|
||||
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);
|
||||
return out;
|
||||
}
|
||||
|
@ -1991,7 +1991,7 @@ public:
|
||||
|
||||
void set_enum_labels(GUIType gui_type, const std::initializer_list<std::string_view> il) {
|
||||
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;
|
||||
enum_def->set_labels(il);
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ public:
|
||||
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 *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 *end() const { return m_end; }
|
||||
|
@ -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.);
|
||||
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());
|
||||
else {
|
||||
// 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)) } } });
|
||||
}
|
||||
#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);
|
||||
}
|
||||
}
|
||||
|
@ -796,8 +796,7 @@ const Glyph* priv::get_glyph(
|
||||
auto glyph_item = cache.find(unicode);
|
||||
if (glyph_item != cache.end()) return &glyph_item->second;
|
||||
|
||||
unsigned int font_index = font_prop.collection_number.has_value()?
|
||||
*font_prop.collection_number : 0;
|
||||
unsigned int font_index = font_prop.collection_number.value_or(0);
|
||||
if (!is_valid(font, font_index)) return nullptr;
|
||||
|
||||
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));
|
||||
}
|
||||
if (font_prop.skew.has_value()) {
|
||||
const float &ratio = *font_prop.skew;
|
||||
auto skew = [&ratio](Polygon &polygon) {
|
||||
for (Slic3r::Point &p : polygon.points) {
|
||||
p.x() += p.y() * ratio;
|
||||
}
|
||||
double ratio = *font_prop.skew;
|
||||
auto skew = [&ratio](Polygon &polygon) {
|
||||
for (Slic3r::Point &p : polygon.points)
|
||||
p.x() += static_cast<Point::coord_type>(std::round(p.y() * ratio));
|
||||
};
|
||||
for (ExPolygon &expolygon : glyph_opt->shape) {
|
||||
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)
|
||||
{
|
||||
const auto &cn = fp.collection_number;
|
||||
unsigned int font_index = (cn.has_value()) ? *cn : 0;
|
||||
int unit_per_em = ff.infos[font_index].unit_per_em;
|
||||
double scale = fp.size_in_mm / unit_per_em;
|
||||
size_t font_index = fp.collection_number.value_or(0);
|
||||
const FontFile::Info &info = ff.infos[font_index];
|
||||
double scale = fp.size_in_mm / (double) info.unit_per_em;
|
||||
// Shape is scaled for store point coordinate as integer
|
||||
return scale * SHAPE_SCALE;
|
||||
}
|
||||
|
@ -10,6 +10,8 @@
|
||||
#include <cassert>
|
||||
#include <list>
|
||||
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
void ExPolygon::scale(double factor)
|
||||
@ -395,7 +397,7 @@ bool has_duplicate_points(const ExPolygon &expoly)
|
||||
size_t cnt = expoly.contour.points.size();
|
||||
for (const Polygon &hole : expoly.holes)
|
||||
cnt += hole.points.size();
|
||||
std::vector<Point> allpts;
|
||||
Points allpts;
|
||||
allpts.reserve(cnt);
|
||||
allpts.insert(allpts.begin(), expoly.contour.points.begin(), expoly.contour.points.end());
|
||||
for (const Polygon &hole : expoly.holes)
|
||||
@ -416,20 +418,36 @@ bool has_duplicate_points(const ExPolygons &expolys)
|
||||
{
|
||||
#if 1
|
||||
// Check globally.
|
||||
size_t cnt = 0;
|
||||
for (const ExPolygon &expoly : expolys) {
|
||||
cnt += expoly.contour.points.size();
|
||||
for (const Polygon &hole : expoly.holes)
|
||||
cnt += hole.points.size();
|
||||
}
|
||||
std::vector<Point> allpts;
|
||||
allpts.reserve(cnt);
|
||||
#if 0
|
||||
// Detect duplicates by sorting with quicksort. It is quite fast, but ankerl::unordered_dense is around 1/4 faster.
|
||||
Points allpts;
|
||||
allpts.reserve(count_points(expolys));
|
||||
for (const ExPolygon &expoly : expolys) {
|
||||
allpts.insert(allpts.begin(), expoly.contour.points.begin(), expoly.contour.points.end());
|
||||
for (const Polygon &hole : expoly.holes)
|
||||
allpts.insert(allpts.end(), hole.points.begin(), hole.points.end());
|
||||
}
|
||||
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
|
||||
// Check per contour.
|
||||
for (const ExPolygon &expoly : expolys)
|
||||
|
@ -867,9 +867,12 @@ namespace Slic3r {
|
||||
IdToCutObjectInfoMap::iterator cut_object_info = m_cut_object_infos.find(object.second + 1);
|
||||
if (cut_object_info != m_cut_object_infos.end()) {
|
||||
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) {
|
||||
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 =
|
||||
ModelVolume::CutInfo(CutConnectorType(connector.type), connector.r_tolerance, connector.h_tolerance, true);
|
||||
}
|
||||
@ -2958,9 +2961,9 @@ namespace Slic3r {
|
||||
|
||||
unsigned int object_cnt = 0;
|
||||
for (const ModelObject* object : model.objects) {
|
||||
object_cnt++;
|
||||
if (!object->is_cut())
|
||||
continue;
|
||||
object_cnt++;
|
||||
pt::ptree& obj_tree = tree.add("objects.object", "");
|
||||
|
||||
obj_tree.put("<xmlattr>.id", object_cnt);
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "pwmx.hpp"
|
||||
#include "AnycubicSLA.hpp"
|
||||
#include "GCode/ThumbnailData.hpp"
|
||||
#include "SLA/RasterBase.hpp"
|
||||
#include "libslic3r/SLAPrint.hpp"
|
||||
@ -22,6 +22,8 @@
|
||||
#define CFG_DELAY_BEFORE_EXPOSURE "DELAY_BEFORE_EXPOSURE"
|
||||
#define CFG_BOTTOM_LIFT_SPEED "BOTTOM_LIFT_SPEED"
|
||||
#define CFG_BOTTOM_LIFT_DISTANCE "BOTTOM_LIFT_DISTANCE"
|
||||
#define CFG_ANTIALIASING "ANTIALIASING"
|
||||
|
||||
|
||||
#define PREV_W 224
|
||||
#define PREV_H 168
|
||||
@ -31,7 +33,7 @@
|
||||
|
||||
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)
|
||||
{
|
||||
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,
|
||||
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_end = src + size;
|
||||
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;
|
||||
// fully transparent of fully opaque pixel
|
||||
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>;
|
||||
|
||||
typedef struct pwmx_format_intro
|
||||
typedef struct anycubicsla_format_intro
|
||||
{
|
||||
char tag[12];
|
||||
std::uint32_t version; // value 1
|
||||
std::uint32_t area_num; // unknown - usually 4
|
||||
std::uint32_t version; // value 1 (also known as 515, 516 and 517)
|
||||
std::uint32_t area_num; // Number of tables - usually 4
|
||||
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::float_t intro32; // unknown
|
||||
std::uint32_t layer_color_offset; // unused in version 1
|
||||
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;
|
||||
} pwmx_format_intro;
|
||||
} anycubicsla_format_intro;
|
||||
|
||||
typedef struct pwmx_format_header
|
||||
typedef struct anycubicsla_format_header
|
||||
{
|
||||
char tag[12];
|
||||
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 print_time_s;
|
||||
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];
|
||||
std::uint32_t payload_size;
|
||||
@ -134,16 +136,16 @@ typedef struct pwmx_format_preview
|
||||
std::uint32_t preview_h;
|
||||
// raw image data in BGR565 format
|
||||
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];
|
||||
std::uint32_t payload_size;
|
||||
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_size;
|
||||
@ -153,20 +155,20 @@ typedef struct pwmx_format_layer
|
||||
std::float_t layer_height_mm;
|
||||
std::float_t layer44; // 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_lift_distance_mm;
|
||||
std::float_t bottom_lift_speed_mms;
|
||||
|
||||
} pwmx_format_misc;
|
||||
} anycubicsla_format_misc;
|
||||
|
||||
class PwmxFormatConfigDef : public ConfigDef
|
||||
class AnycubicSLAFormatConfigDef : public ConfigDef
|
||||
{
|
||||
public:
|
||||
PwmxFormatConfigDef()
|
||||
AnycubicSLAFormatConfigDef()
|
||||
{
|
||||
add(CFG_LIFT_DISTANCE, coFloat);
|
||||
add(CFG_LIFT_SPEED, coFloat);
|
||||
@ -174,17 +176,18 @@ public:
|
||||
add(CFG_DELAY_BEFORE_EXPOSURE, coFloat);
|
||||
add(CFG_BOTTOM_LIFT_DISTANCE, coFloat);
|
||||
add(CFG_BOTTOM_LIFT_SPEED, coFloat);
|
||||
add(CFG_ANTIALIASING, coInt);
|
||||
}
|
||||
};
|
||||
|
||||
class PwmxFormatDynamicConfig : public DynamicConfig
|
||||
class AnycubicSLAFormatDynamicConfig : public DynamicConfig
|
||||
{
|
||||
public:
|
||||
PwmxFormatDynamicConfig(){};
|
||||
AnycubicSLAFormatDynamicConfig(){};
|
||||
const ConfigDef *def() const override { return &config_def; }
|
||||
|
||||
private:
|
||||
PwmxFormatConfigDef config_def;
|
||||
AnycubicSLAFormatConfigDef config_def;
|
||||
};
|
||||
|
||||
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,
|
||||
pwmx_format_misc &/*m*/,
|
||||
void fill_preview(anycubicsla_format_preview &p,
|
||||
anycubicsla_format_misc &/*m*/,
|
||||
const ThumbnailsList &thumbnails)
|
||||
{
|
||||
|
||||
@ -266,9 +269,8 @@ void fill_preview(pwmx_format_preview &p,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void fill_header(pwmx_format_header &h,
|
||||
pwmx_format_misc &m,
|
||||
void fill_header(anycubicsla_format_header &h,
|
||||
anycubicsla_format_misc &m,
|
||||
const SLAPrint &print,
|
||||
std::uint32_t layer_count)
|
||||
{
|
||||
@ -282,7 +284,7 @@ void fill_header(pwmx_format_header &h,
|
||||
auto mat_opt = cfg.option("material_notes");
|
||||
std::string mnotes = mat_opt? cfg.option("material_notes")->serialize() : "";
|
||||
// create a config parser from the material notes
|
||||
Slic3r::PwmxFormatDynamicConfig mat_cfg;
|
||||
Slic3r::AnycubicSLAFormatDynamicConfig mat_cfg;
|
||||
SLAPrintStatistics stats = print.print_statistics();
|
||||
|
||||
// sanitize the string config
|
||||
@ -314,6 +316,13 @@ void fill_header(pwmx_format_header &h,
|
||||
h.per_layer_override = 0;
|
||||
|
||||
// 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);
|
||||
crop_value(h.delay_before_exposure_s, 0.0f, 1000.0f);
|
||||
|
||||
@ -356,7 +365,7 @@ void fill_header(pwmx_format_header &h,
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<sla::RasterBase> PwmxArchive::create_raster() const
|
||||
std::unique_ptr<sla::RasterBase> AnycubicSLAArchive::create_raster() const
|
||||
{
|
||||
sla::Resolution res;
|
||||
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);
|
||||
}
|
||||
|
||||
sla::RasterEncoder PwmxArchive::get_encoder() const
|
||||
sla::RasterEncoder AnycubicSLAArchive::get_encoder() const
|
||||
{
|
||||
return PWXRasterEncoder{};
|
||||
return AnycubicSLARasterEncoder{};
|
||||
}
|
||||
|
||||
// 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 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 *) &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;
|
||||
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));
|
||||
pwmx_write_int32(out, i.version);
|
||||
pwmx_write_int32(out, i.area_num);
|
||||
pwmx_write_int32(out, i.header_data_offset);
|
||||
pwmx_write_int32(out, i.intro24);
|
||||
pwmx_write_int32(out, i.preview_data_offset);
|
||||
pwmx_write_int32(out, i.intro32);
|
||||
pwmx_write_int32(out, i.layer_data_offset);
|
||||
pwmx_write_int32(out, i.intro40);
|
||||
pwmx_write_int32(out, i.image_data_offset);
|
||||
anycubicsla_write_int32(out, i.version);
|
||||
anycubicsla_write_int32(out, i.area_num);
|
||||
anycubicsla_write_int32(out, i.header_data_offset);
|
||||
anycubicsla_write_int32(out, i.software_data_offset);
|
||||
anycubicsla_write_int32(out, i.preview_data_offset);
|
||||
anycubicsla_write_int32(out, i.layer_color_offset);
|
||||
anycubicsla_write_int32(out, i.layer_data_offset);
|
||||
anycubicsla_write_int32(out, i.extra_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));
|
||||
pwmx_write_int32(out, h.payload_size);
|
||||
pwmx_write_float(out, h.pixel_size_um);
|
||||
pwmx_write_float(out, h.layer_height_mm);
|
||||
pwmx_write_float(out, h.exposure_time_s);
|
||||
pwmx_write_float(out, h.delay_before_exposure_s);
|
||||
pwmx_write_float(out, h.bottom_exposure_time_s);
|
||||
pwmx_write_float(out, h.bottom_layer_count);
|
||||
pwmx_write_float(out, h.lift_distance_mm);
|
||||
pwmx_write_float(out, h.lift_speed_mms);
|
||||
pwmx_write_float(out, h.retract_speed_mms);
|
||||
pwmx_write_float(out, h.volume_ml);
|
||||
pwmx_write_int32(out, h.antialiasing);
|
||||
pwmx_write_int32(out, h.res_x);
|
||||
pwmx_write_int32(out, h.res_y);
|
||||
pwmx_write_float(out, h.weight_g);
|
||||
pwmx_write_float(out, h.price);
|
||||
pwmx_write_int32(out, h.price_currency);
|
||||
pwmx_write_int32(out, h.per_layer_override);
|
||||
pwmx_write_int32(out, h.print_time_s);
|
||||
pwmx_write_int32(out, h.transition_layer_count);
|
||||
pwmx_write_int32(out, h.unknown);
|
||||
anycubicsla_write_int32(out, h.payload_size);
|
||||
anycubicsla_write_float(out, h.pixel_size_um);
|
||||
anycubicsla_write_float(out, h.layer_height_mm);
|
||||
anycubicsla_write_float(out, h.exposure_time_s);
|
||||
anycubicsla_write_float(out, h.delay_before_exposure_s);
|
||||
anycubicsla_write_float(out, h.bottom_exposure_time_s);
|
||||
anycubicsla_write_float(out, h.bottom_layer_count);
|
||||
anycubicsla_write_float(out, h.lift_distance_mm);
|
||||
anycubicsla_write_float(out, h.lift_speed_mms);
|
||||
anycubicsla_write_float(out, h.retract_speed_mms);
|
||||
anycubicsla_write_float(out, h.volume_ml);
|
||||
anycubicsla_write_int32(out, h.antialiasing);
|
||||
anycubicsla_write_int32(out, h.res_x);
|
||||
anycubicsla_write_int32(out, h.res_y);
|
||||
anycubicsla_write_float(out, h.weight_g);
|
||||
anycubicsla_write_float(out, h.price);
|
||||
anycubicsla_write_int32(out, h.price_currency);
|
||||
anycubicsla_write_int32(out, h.per_layer_override);
|
||||
anycubicsla_write_int32(out, h.print_time_s);
|
||||
anycubicsla_write_int32(out, h.transition_layer_count);
|
||||
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));
|
||||
pwmx_write_int32(out, p.payload_size);
|
||||
pwmx_write_int32(out, p.preview_w);
|
||||
pwmx_write_int32(out, p.preview_dpi);
|
||||
pwmx_write_int32(out, p.preview_h);
|
||||
anycubicsla_write_int32(out, p.payload_size);
|
||||
anycubicsla_write_int32(out, p.preview_w);
|
||||
anycubicsla_write_int32(out, p.preview_dpi);
|
||||
anycubicsla_write_int32(out, p.preview_h);
|
||||
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));
|
||||
pwmx_write_int32(out, h.payload_size);
|
||||
pwmx_write_int32(out, h.layer_count);
|
||||
anycubicsla_write_int32(out, h.payload_size);
|
||||
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);
|
||||
pwmx_write_int32(out, l.image_size);
|
||||
pwmx_write_float(out, l.lift_distance_mm);
|
||||
pwmx_write_float(out, l.lift_speed_mms);
|
||||
pwmx_write_float(out, l.exposure_time_s);
|
||||
pwmx_write_float(out, l.layer_height_mm);
|
||||
pwmx_write_float(out, l.layer44);
|
||||
pwmx_write_float(out, l.layer48);
|
||||
anycubicsla_write_int32(out, l.image_offset);
|
||||
anycubicsla_write_int32(out, l.image_size);
|
||||
anycubicsla_write_float(out, l.lift_distance_mm);
|
||||
anycubicsla_write_float(out, l.lift_speed_mms);
|
||||
anycubicsla_write_float(out, l.exposure_time_s);
|
||||
anycubicsla_write_float(out, l.layer_height_mm);
|
||||
anycubicsla_write_float(out, l.layer44);
|
||||
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 ThumbnailsList &thumbnails,
|
||||
const std::string &/*projectname*/)
|
||||
{
|
||||
std::uint32_t layer_count = m_layers.size();
|
||||
|
||||
pwmx_format_intro intro = {};
|
||||
pwmx_format_header header = {};
|
||||
pwmx_format_preview preview = {};
|
||||
pwmx_format_layers_header layers_header = {};
|
||||
pwmx_format_misc misc = {};
|
||||
anycubicsla_format_intro intro = {};
|
||||
anycubicsla_format_header header = {};
|
||||
anycubicsla_format_preview preview = {};
|
||||
anycubicsla_format_layers_header layers_header = {};
|
||||
anycubicsla_format_misc misc = {};
|
||||
std::vector<uint8_t> layer_images;
|
||||
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.header_data_offset = sizeof(intro);
|
||||
intro.preview_data_offset = sizeof(intro) + sizeof(header);
|
||||
intro.layer_data_offset = intro.preview_data_offset + sizeof(preview);
|
||||
intro.image_data_offset = intro.layer_data_offset +
|
||||
sizeof(layers_header) +
|
||||
(sizeof(pwmx_format_layer) * layer_count);
|
||||
(sizeof(anycubicsla_format_layer) * layer_count);
|
||||
|
||||
fill_header(header, misc, print, layer_count);
|
||||
fill_preview(preview, misc, thumbnails);
|
||||
@ -513,21 +524,21 @@ void PwmxArchive::export_print(const std::string fname,
|
||||
// open the file and write the contents
|
||||
std::ofstream out;
|
||||
out.open(fname, std::ios::binary | std::ios::out | std::ios::trunc);
|
||||
pwmx_write_intro(out, intro);
|
||||
pwmx_write_header(out, header);
|
||||
pwmx_write_preview(out, preview);
|
||||
anycubicsla_write_intro(out, intro);
|
||||
anycubicsla_write_header(out, header);
|
||||
anycubicsla_write_preview(out, preview);
|
||||
|
||||
layers_header.payload_size = intro.image_data_offset - intro.layer_data_offset -
|
||||
sizeof(layers_header.tag) - sizeof(layers_header.payload_size);
|
||||
layers_header.layer_count = layer_count;
|
||||
pwmx_write_layers_header(out, layers_header);
|
||||
anycubicsla_write_layers_header(out, layers_header);
|
||||
|
||||
//layers
|
||||
layer_images.reserve(layer_count * LAYER_SIZE_ESTIMATE);
|
||||
image_offset = intro.image_data_offset;
|
||||
size_t i = 0;
|
||||
for (const sla::EncodedRaster &rst : m_layers) {
|
||||
pwmx_format_layer l;
|
||||
anycubicsla_format_layer l;
|
||||
std::memset(&l, 0, sizeof(l));
|
||||
l.image_offset = image_offset;
|
||||
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;
|
||||
}
|
||||
image_offset += l.image_size;
|
||||
pwmx_write_layer(out, l);
|
||||
anycubicsla_write_layer(out, l);
|
||||
// add the rle encoded layer image into the buffer
|
||||
const char* img_start = reinterpret_cast<const char*>(rst.data());
|
||||
const char* img_end = img_start + rst.size();
|
81
src/libslic3r/Format/AnycubicSLA.hpp
Normal file
81
src/libslic3r/Format/AnycubicSLA.hpp
Normal 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_
|
@ -2,7 +2,7 @@
|
||||
|
||||
#include "SL1.hpp"
|
||||
#include "SL1_SVG.hpp"
|
||||
#include "pwmx.hpp"
|
||||
#include "AnycubicSLA.hpp"
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
|
||||
@ -33,10 +33,9 @@ static const std::map<std::string, ArchiveEntry> REGISTERED_ARCHIVES {
|
||||
"SL2",
|
||||
{ "sl1_svg", [] (const auto &cfg) { return std::make_unique<SL1_SVGArchive>(cfg); } }
|
||||
},
|
||||
{
|
||||
"pwmx",
|
||||
{ "pwmx", [] (const auto &cfg) { return std::make_unique<PwmxArchive>(cfg); } }
|
||||
}
|
||||
ANYCUBIC_SLA_FORMAT("pwmo", "Photon Mono"),
|
||||
ANYCUBIC_SLA_FORMAT("pwmx", "Photon Mono X"),
|
||||
ANYCUBIC_SLA_FORMAT("pwms", "Photon Mono SE"),
|
||||
};
|
||||
|
||||
std::unique_ptr<SLAArchiveWriter>
|
||||
|
@ -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_
|
@ -836,6 +836,7 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
|
||||
path_tmp += ".tmp";
|
||||
|
||||
m_processor.initialize(path_tmp);
|
||||
m_processor.set_print(print);
|
||||
GCodeOutputStream file(boost::nowide::fopen(path_tmp.c_str(), "wb"), m_processor);
|
||||
if (! file.is_open())
|
||||
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).
|
||||
const bool print_wipe_extrusions)
|
||||
{
|
||||
//FIXME what the heck ID is this? Layer ID or Object ID? More likely an Object ID.
|
||||
uint32_t layer_id = 0;
|
||||
bool first = true;
|
||||
bool first = true;
|
||||
int object_id = 0;
|
||||
// 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) {
|
||||
first = false;
|
||||
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_last_obj_copy = this_object_copy;
|
||||
this->set_origin(unscale(offset));
|
||||
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";
|
||||
if (this->config().gcode_label_objects) {
|
||||
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)
|
||||
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)
|
||||
@ -3020,10 +3026,17 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de
|
||||
{100, ConfigOptionInts{0}}};
|
||||
}
|
||||
|
||||
double external_perim_reference_speed = std::min(m_config.get_abs_value("external_perimeter_speed"),
|
||||
std::min(EXTRUDER_CONFIG(filament_max_volumetric_speed) / path.mm3_per_mm,
|
||||
m_config.max_volumetric_speed.value / path.mm3_per_mm));
|
||||
new_points = m_extrusion_quality_estimator.estimate_extrusion_quality(path, overhangs_with_speeds, overhang_w_fan_speeds,
|
||||
double external_perim_reference_speed = m_config.get_abs_value("external_perimeter_speed");
|
||||
if (external_perim_reference_speed == 0)
|
||||
external_perim_reference_speed = m_volumetric_speed / path.mm3_per_mm;
|
||||
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,
|
||||
speed);
|
||||
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;
|
||||
if (m_enable_cooling_markers) {
|
||||
if (path.role().is_bridge() &&
|
||||
(!path.role().is_perimeter() || !this->config().enable_dynamic_fan_speeds.get_at(m_writer.extruder()->id())))
|
||||
if (path.role().is_bridge())
|
||||
gcode += ";_BRIDGE_FAN_START\n";
|
||||
else
|
||||
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_fan_speed = new_points[0].fan_speed;
|
||||
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);
|
||||
for (size_t i = 1; i < new_points.size(); 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) {
|
||||
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)
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "../ClipperUtils.hpp"
|
||||
#include "../Flow.hpp"
|
||||
#include "../Config.hpp"
|
||||
#include "../Line.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
@ -20,107 +21,32 @@
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <ostream>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
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
|
||||
{
|
||||
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;
|
||||
float distance;
|
||||
size_t nearest_prev_layer_line;
|
||||
float curvature;
|
||||
};
|
||||
|
||||
template<bool SCALED_INPUT, bool ADD_INTERSECTIONS, bool PREV_LAYER_BOUNDARY_OFFSET, bool SIGNED_DISTANCE, typename P, typename L>
|
||||
std::vector<ExtendedPoint> estimate_points_properties(const std::vector<P> &input_points,
|
||||
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 POINTS &input_points,
|
||||
const AABBTreeLines::LinesDistancer<L> &unscaled_prev_layer,
|
||||
float flow_width,
|
||||
float max_line_length = -1.0f)
|
||||
{
|
||||
using P = typename POINTS::value_type;
|
||||
|
||||
using AABBScalar = typename AABBTreeLines::LinesDistancer<L>::Scalar;
|
||||
if (input_points.empty())
|
||||
return {};
|
||||
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>(); };
|
||||
|
||||
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())};
|
||||
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.nearest_prev_layer_line = nearest_line;
|
||||
points.push_back(start_point);
|
||||
}
|
||||
for (size_t i = 1; i < input_points.size(); 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>());
|
||||
next_point.distance = distance + boundary_offset;
|
||||
next_point.nearest_prev_layer_line = nearest_line;
|
||||
|
||||
if (ADD_INTERSECTIONS &&
|
||||
((points.back().distance > boundary_offset + EPSILON) != (next_point.distance > boundary_offset + EPSILON))) {
|
||||
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>()});
|
||||
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);
|
||||
@ -170,12 +97,18 @@ std::vector<ExtendedPoint> estimate_points_properties(const std::vector<P>
|
||||
if (t0 < 1.0) {
|
||||
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>());
|
||||
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) {
|
||||
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>());
|
||||
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);
|
||||
auto [p_dist, p_near_l,
|
||||
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());
|
||||
@ -207,6 +143,9 @@ std::vector<ExtendedPoint> estimate_points_properties(const std::vector<P>
|
||||
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) {
|
||||
ExtendedPoint &a = points[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;
|
||||
while (prev_point_idx > 0) {
|
||||
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;
|
||||
while (next_point_index < int(points.size()) - 1) {
|
||||
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) {
|
||||
float distance = (prev.position - a.position).norm();
|
||||
float alfa = angle(a.position - points[prev_point_idx].position, points[next_point_index].position - a.position);
|
||||
cestim.add_point(distance, alfa);
|
||||
}
|
||||
float alfa = angle(a.position - points[prev_point_idx].position, points[next_point_index].position - a.position);
|
||||
angles_for_curvature[point_idx] = 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;
|
||||
@ -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>> 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;
|
||||
|
||||
public:
|
||||
@ -253,18 +231,22 @@ public:
|
||||
|
||||
void prepare_for_new_layer(const Layer *layer)
|
||||
{
|
||||
if (layer == nullptr) return;
|
||||
const PrintObject *object = layer->object();
|
||||
prev_layer_boundaries[object] = next_layer_boundaries[object];
|
||||
next_layer_boundaries[object] = AABBTreeLines::LinesDistancer<Linef>{to_unscaled_linesf(layer->lslices)};
|
||||
if (layer == nullptr)
|
||||
return;
|
||||
const PrintObject *object = layer->object();
|
||||
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,
|
||||
const std::vector<std::pair<int, ConfigOptionFloatOrPercent>> overhangs_w_speeds,
|
||||
const std::vector<std::pair<int, ConfigOptionInts>> overhangs_w_fan_speeds,
|
||||
size_t extruder_id,
|
||||
float ext_perimeter_speed,
|
||||
float original_speed)
|
||||
std::vector<ProcessedPoint> estimate_speed_from_extrusion_quality(
|
||||
const ExtrusionPath &path,
|
||||
const std::vector<std::pair<int, ConfigOptionFloatOrPercent>> overhangs_w_speeds,
|
||||
const std::vector<std::pair<int, ConfigOptionInts>> overhangs_w_fan_speeds,
|
||||
size_t extruder_id,
|
||||
float ext_perimeter_speed,
|
||||
float original_speed)
|
||||
{
|
||||
float speed_base = ext_perimeter_speed > 0 ? ext_perimeter_speed : original_speed;
|
||||
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 speed = overhangs_w_speeds[i].second.percent ? (speed_base * overhangs_w_speeds[i].second.value / 100.0) :
|
||||
overhangs_w_speeds[i].second.value;
|
||||
if (speed < EPSILON) speed = speed_base;
|
||||
speed_sections[distance] = speed;
|
||||
}
|
||||
|
||||
@ -291,6 +274,22 @@ public:
|
||||
const ExtendedPoint &curr = extended_points[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 upper_dist = values.lower_bound(distance);
|
||||
if (upper_dist == values.end()) {
|
||||
@ -305,12 +304,14 @@ public:
|
||||
return (1.0f - t) * lower_dist->second + t * upper_dist->second;
|
||||
};
|
||||
|
||||
float extrusion_speed = std::min(interpolate_speed(speed_sections, curr.distance),
|
||||
interpolate_speed(speed_sections, next.distance));
|
||||
float fan_speed = std::min(interpolate_speed(fan_speed_sections, curr.distance),
|
||||
interpolate_speed(fan_speed_sections, next.distance));
|
||||
float extrusion_speed = std::min(interpolate_speed(speed_sections, curr.distance),
|
||||
interpolate_speed(speed_sections, next.distance));
|
||||
float curled_base_speed = interpolate_speed(speed_sections, artificial_distance_to_curled_lines);
|
||||
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;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/LocalesUtils.hpp"
|
||||
#include "libslic3r/format.hpp"
|
||||
#include "libslic3r/I18N.hpp"
|
||||
#include "libslic3r/GCodeWriter.hpp"
|
||||
#include "GCodeProcessor.hpp"
|
||||
|
||||
@ -441,9 +442,7 @@ void GCodeProcessorResult::reset() {
|
||||
max_print_height = 0.0f;
|
||||
settings_ids.reset();
|
||||
extruders_count = 0;
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
backtrace_enabled = false;
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
extruder_colors = std::vector<std::string>();
|
||||
filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
|
||||
filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY);
|
||||
@ -461,9 +460,7 @@ void GCodeProcessorResult::reset() {
|
||||
max_print_height = 0.0f;
|
||||
settings_ids.reset();
|
||||
extruders_count = 0;
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
backtrace_enabled = false;
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
extruder_colors = std::vector<std::string>();
|
||||
filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
|
||||
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_flavor = config.gcode_flavor;
|
||||
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
m_result.backtrace_enabled = is_XL_printer(config);
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
|
||||
size_t extruders_count = config.nozzle_diameter.values.size();
|
||||
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_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()))
|
||||
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 {
|
||||
unsigned char old_extruder_id = m_extruder_id;
|
||||
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);
|
||||
}
|
||||
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
// Helper class to modify and export gcode to file
|
||||
class ExportLines
|
||||
{
|
||||
@ -3566,7 +3560,7 @@ void GCodeProcessor::post_process()
|
||||
++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;
|
||||
}
|
||||
|
||||
@ -3610,7 +3604,7 @@ void GCodeProcessor::post_process()
|
||||
last_time_insertion = rev_it->time;
|
||||
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;
|
||||
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
|
||||
m_statistics.add_line(out_line.length());
|
||||
#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]);
|
||||
#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
|
||||
// gcode_line is in/out parameter, to reduce expensive memory allocation
|
||||
auto process_placeholders = [&](std::string& gcode_line) {
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
bool processed = false;
|
||||
#else
|
||||
unsigned int extra_lines_count = 0;
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
|
||||
// remove trailing '\n'
|
||||
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) {
|
||||
line = line.substr(1);
|
||||
if (m_time_processor.export_remaining_time_enabled &&
|
||||
@ -3739,29 +3722,16 @@ void GCodeProcessor::post_process()
|
||||
const TimeMachine& machine = m_time_processor.machines[i];
|
||||
if (machine.enabled) {
|
||||
// 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(),
|
||||
(line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? 0 : 100,
|
||||
(line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? time_in_minutes(machine.time) : 0));
|
||||
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
|
||||
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);
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
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;
|
||||
#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",
|
||||
(mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent",
|
||||
get_time_dhms(machine.time).c_str());
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
export_lines.append_line(buf);
|
||||
processed = true;
|
||||
#else
|
||||
ret += buf;
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
}
|
||||
}
|
||||
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",
|
||||
(mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent",
|
||||
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);
|
||||
processed = true;
|
||||
#else
|
||||
ret += buf;
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
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);
|
||||
@ -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,
|
||||
// Caches, to be modified
|
||||
&g1_times_cache_it, &last_exported_main, &last_exported_stop,
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
&export_lines]
|
||||
#else
|
||||
// String output
|
||||
&export_line]
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
(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) {
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++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),
|
||||
time_in_minutes(machine.time - it->elapsed_time) };
|
||||
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(),
|
||||
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;
|
||||
#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
++exported_lines_count;
|
||||
#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
}
|
||||
// export remaining time to next printer stop
|
||||
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 (to_export_stop > 0) {
|
||||
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));
|
||||
#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;
|
||||
#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
++exported_lines_count;
|
||||
#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -3953,22 +3885,12 @@ void GCodeProcessor::post_process()
|
||||
}
|
||||
|
||||
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))
|
||||
export_lines.append_line(format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop));
|
||||
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)));
|
||||
#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;
|
||||
#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
|
||||
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);
|
||||
@ -3991,6 +3909,18 @@ void GCodeProcessor::post_process()
|
||||
int tool_number = -1;
|
||||
ss >> tool_number;
|
||||
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,
|
||||
// line inserter
|
||||
[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();
|
||||
#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;
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
// Backtrace data for Tx gcode lines
|
||||
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
|
||||
// to flush the backtrace cache accordingly
|
||||
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.
|
||||
@ -4069,24 +3977,15 @@ void GCodeProcessor::post_process()
|
||||
gcode_line.insert(gcode_line.end(), it, it_end);
|
||||
if (eol) {
|
||||
++line_id;
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
export_lines.update(line_id, g1_lines_counter);
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
|
||||
gcode_line += "\n";
|
||||
// replace placeholder lines
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
bool processed = process_placeholders(gcode_line);
|
||||
if (processed)
|
||||
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)
|
||||
processed = process_used_filament(gcode_line);
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
if (!processed && !is_temporary_decoration(gcode_line)) {
|
||||
if (GCodeReader::GCodeLine::cmd_is(gcode_line, "G1"))
|
||||
// add lines M73 where needed
|
||||
@ -4101,18 +4000,6 @@ void GCodeProcessor::post_process()
|
||||
if (!gcode_line.empty())
|
||||
export_lines.append_line(gcode_line);
|
||||
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();
|
||||
}
|
||||
// Skip EOL.
|
||||
@ -4127,30 +4014,12 @@ void GCodeProcessor::post_process()
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
export_lines.flush(out, m_result, out_path);
|
||||
#else
|
||||
if (!export_line.empty())
|
||||
write_string(export_line);
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
|
||||
out.close();
|
||||
in.close();
|
||||
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
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))
|
||||
throw Slic3r::RuntimeError(std::string("Failed to rename the output G-code file from ") + out_path + " to " + m_result.filename + '\n' +
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Print;
|
||||
|
||||
enum class EMoveType : unsigned char
|
||||
{
|
||||
Noop,
|
||||
@ -125,9 +127,7 @@ namespace Slic3r {
|
||||
float max_print_height;
|
||||
SettingsIds settings_ids;
|
||||
size_t extruders_count;
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
bool backtrace_enabled;
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
std::vector<std::string> extruder_colors;
|
||||
std::vector<float> filament_diameters;
|
||||
std::vector<float> filament_densities;
|
||||
@ -588,6 +588,8 @@ namespace Slic3r {
|
||||
TimeProcessor m_time_processor;
|
||||
UsedFilaments m_used_filaments;
|
||||
|
||||
Print* m_print{ nullptr };
|
||||
|
||||
GCodeProcessorResult m_result;
|
||||
static unsigned int s_result_id;
|
||||
|
||||
@ -601,6 +603,8 @@ namespace Slic3r {
|
||||
GCodeProcessor();
|
||||
|
||||
void apply_config(const PrintConfig& config);
|
||||
void set_print(Print* print) { m_print = print; }
|
||||
|
||||
void enable_stealth_time_estimator(bool enabled);
|
||||
bool is_stealth_time_estimator_enabled() const {
|
||||
return m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].enabled;
|
||||
|
@ -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
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
@ -71,7 +71,6 @@ public:
|
||||
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) {
|
||||
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();
|
||||
return { cmd.begin(), cmd.end() };
|
||||
}
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
|
||||
private:
|
||||
std::string m_raw;
|
||||
|
@ -12,11 +12,6 @@
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace ClipperLib {
|
||||
class PolyNode;
|
||||
using PolyNodes = std::vector<PolyNode*>;
|
||||
}
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
// Generic result of an orientation predicate.
|
||||
|
@ -106,6 +106,7 @@ Slic3r::Pointfs Slic3r::intersection_points(const ExPolygons &expolygons)
|
||||
#include <libslic3r/BoundingBox.hpp>
|
||||
|
||||
namespace priv {
|
||||
//FIXME O(n^2) complexity!
|
||||
Slic3r::Pointfs compute_intersections(const Slic3r::Lines &lines)
|
||||
{
|
||||
using namespace Slic3r;
|
||||
|
@ -6,6 +6,7 @@
|
||||
namespace Slic3r {
|
||||
|
||||
// collect all intersecting points
|
||||
//FIXME O(n^2) complexity!
|
||||
Pointfs intersection_points(const Lines &lines);
|
||||
Pointfs intersection_points(const Polygon &polygon);
|
||||
Pointfs intersection_points(const Polygons &polygons);
|
||||
|
@ -19,6 +19,8 @@
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <oneapi/tbb/scalable_allocator.h>
|
||||
|
||||
//#define DEBUG_FILES
|
||||
#ifdef DEBUG_FILES
|
||||
#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;
|
||||
Lines obstacles;
|
||||
obstacles.reserve(layer->malformed_lines.size());
|
||||
for (const Line &l : layer->malformed_lines) { obstacles.push_back(Line{l.a + global_origin, l.b + global_origin}); }
|
||||
obstacles.reserve(layer->curled_lines.size());
|
||||
for (const Line &l : layer->curled_lines) { obstacles.push_back(Line{l.a + global_origin, l.b + global_origin}); }
|
||||
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)>>;
|
||||
|
||||
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;
|
||||
|
||||
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));
|
||||
#endif
|
||||
|
||||
std::vector<Pixel> tmp_path;
|
||||
std::vector<Pixel, PointsAllocator<Pixel>> tmp_path;
|
||||
tmp_path.reserve(out_path.size());
|
||||
// Some path found, reverse and remove points that do not change direction
|
||||
std::reverse(out_path.begin(), out_path.end());
|
||||
|
@ -60,7 +60,10 @@ void Layer::make_slices()
|
||||
}
|
||||
|
||||
// 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;
|
||||
for (const ExPolygon &expolygon : expolygons)
|
||||
@ -69,15 +72,89 @@ void Layer::make_slices()
|
||||
ClipperLib_Z::Paths out;
|
||||
out.reserve(num_paths);
|
||||
|
||||
for (const ExPolygon &expolygon : expolygons) {
|
||||
for (size_t icontour = 0; icontour < expolygon.num_contours(); ++ icontour) {
|
||||
const Polygon &contour = expolygon.contour_or_hole(icontour);
|
||||
out.emplace_back();
|
||||
ClipperLib_Z::Path &path = out.back();
|
||||
path.reserve(contour.size());
|
||||
for (const Point &p : contour.points)
|
||||
path.push_back({ p.x(), p.y(), isrc });
|
||||
ClipperLib::Paths contours;
|
||||
ClipperLib::Paths holes;
|
||||
ClipperLib::Clipper clipper;
|
||||
ClipperLib::ClipperOffset co;
|
||||
ClipperLib::Paths out2;
|
||||
|
||||
// Top / bottom surfaces must overlap more than 2um to be chained into a Z graph.
|
||||
// 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;
|
||||
}
|
||||
|
||||
@ -126,120 +203,36 @@ static void connect_layer_slices(
|
||||
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.
|
||||
// Otherwise the contour is fully inside another contour.
|
||||
int32_t i = -1, j = -1;
|
||||
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) {
|
||||
j = pt.z();
|
||||
if (j < 0) {
|
||||
const auto &intersection = m_intersections[-j - 1];
|
||||
assert(intersection.first <= intersection.second);
|
||||
if (intersection.second < m_offset_above) {
|
||||
// Ignore intersection of polygons on the 1st layer.
|
||||
assert(intersection.first >= m_offset_below);
|
||||
j = i;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
auto [i, j] = this->find_top_bottom_contour_ids_strict(polynode);
|
||||
bool found = false;
|
||||
if (i < 0 && j < 0) {
|
||||
// This should not happen. It may only happen if the source contours had just self intersections or intersections with contours at the same layer.
|
||||
// We may safely ignore such cases where the intersection area is meager.
|
||||
double a = ClipperLib_Z::Area(polynode.Contour);
|
||||
if (a < sqr(scaled<double>(0.001))) {
|
||||
// Ignore tiny overlaps. They are not worth resolving.
|
||||
} else {
|
||||
// We should not ignore large cases. Try to resolve the conflict by a majority of references.
|
||||
std::tie(i, j) = this->find_top_bottom_contour_ids_approx(polynode);
|
||||
// At least top or bottom should be resolved.
|
||||
assert(i >= 0 || j >= 0);
|
||||
}
|
||||
}
|
||||
end:
|
||||
bool found = false;
|
||||
if (i == -1) {
|
||||
// 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);
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (j < 0) {
|
||||
if (i < 0) {
|
||||
// this->find_top_bottom_contour_ids_approx() shoudl have made sure this does not happen.
|
||||
assert(false);
|
||||
} else {
|
||||
// Index of an island above. Look-it up in the island below.
|
||||
assert(j < m_offset_end);
|
||||
j -= m_offset_above;
|
||||
for (int l = int(m_below.lslices_ex.size()) - 1; l >= 0; -- l) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(i >= m_offset_below && i < m_offset_above);
|
||||
i -= m_offset_below;
|
||||
j = this->find_other_contour_costly(polynode, m_above, j == -2);
|
||||
found = j >= 0;
|
||||
}
|
||||
} 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 {
|
||||
assert(assert_intersection_valid(i, j));
|
||||
i -= m_offset_below;
|
||||
@ -249,6 +242,8 @@ static void connect_layer_slices(
|
||||
found = true;
|
||||
}
|
||||
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.
|
||||
double area = ClipperLib_Z::Area(polynode.Contour);
|
||||
for (int icontour = 0; icontour < polynode.ChildCount(); ++ icontour)
|
||||
@ -288,6 +283,187 @@ static void connect_layer_slices(
|
||||
}
|
||||
|
||||
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;
|
||||
Layer &m_below;
|
||||
Layer &m_above;
|
||||
@ -340,9 +516,9 @@ static void connect_layer_slices(
|
||||
void Layer::build_up_down_graph(Layer& below, Layer& above)
|
||||
{
|
||||
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());
|
||||
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
|
||||
coord_t paths_end = paths_above_offset + coord_t(above.lslices.size());
|
||||
#endif // NDEBUG
|
||||
|
@ -1,6 +1,7 @@
|
||||
#ifndef slic3r_Layer_hpp_
|
||||
#define slic3r_Layer_hpp_
|
||||
|
||||
#include "Line.hpp"
|
||||
#include "libslic3r.h"
|
||||
#include "BoundingBox.hpp"
|
||||
#include "Flow.hpp"
|
||||
@ -325,7 +326,7 @@ public:
|
||||
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.
|
||||
Lines malformed_lines;
|
||||
CurledLines curled_lines;
|
||||
|
||||
// Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry
|
||||
// (with possibly differing extruder ID and slicing parameters) and merged.
|
||||
|
@ -209,6 +209,18 @@ public:
|
||||
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
|
||||
{
|
||||
public:
|
||||
|
@ -837,13 +837,10 @@ ModelInstance* ModelObject::add_instance(const ModelInstance &other)
|
||||
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();
|
||||
instance->set_offset(offset);
|
||||
instance->set_scaling_factor(scaling_factor);
|
||||
instance->set_rotation(rotation);
|
||||
instance->set_mirror(mirror);
|
||||
ModelInstance* instance = add_instance();
|
||||
instance->set_transformation(trafo);
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
@ -392,7 +392,7 @@ public:
|
||||
|
||||
ModelInstance* add_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_last_instance();
|
||||
void clear_instances();
|
||||
|
@ -103,9 +103,9 @@ bool MultiPoint::remove_duplicate_points()
|
||||
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;
|
||||
if (! pts.empty()) {
|
||||
const Point *anchor = &pts.front();
|
||||
|
@ -110,7 +110,7 @@ public:
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
inline double length(const Points &pts) {
|
||||
|
@ -39,11 +39,11 @@
|
||||
#include <ostream>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
// #define ARACHNE_DEBUG
|
||||
|
||||
#ifdef ARACHNE_DEBUG
|
||||
@ -569,7 +569,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator::P
|
||||
size_t occurrence = 0;
|
||||
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) {
|
||||
++point_occurrence[path.polyline.first_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())
|
||||
return polylines;
|
||||
|
||||
std::unordered_map<size_t, Polyline> connected;
|
||||
ankerl::unordered_dense::map<size_t, Polyline> connected;
|
||||
connected.reserve(polylines.size());
|
||||
for (size_t i = 0; i < polylines.size(); i++) {
|
||||
if (!polylines[i].empty()) {
|
||||
@ -731,7 +731,7 @@ ExtrusionPaths sort_extra_perimeters(ExtrusionPaths extra_perims, int index_of_f
|
||||
{
|
||||
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 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)) {
|
||||
@ -1153,11 +1153,11 @@ void PerimeterGenerator::process_arachne(
|
||||
// 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<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++)
|
||||
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) {
|
||||
auto after_it = map_extrusion_to_idx.find(after);
|
||||
++blocked[after_it->second];
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "PlaceholderParser.hpp"
|
||||
#include "Exception.hpp"
|
||||
#include "Flow.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#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(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(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) :
|
||||
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 }
|
||||
@ -904,9 +906,12 @@ namespace client
|
||||
const ConfigOption *opt = ctx->resolve_symbol(opt_key_str);
|
||||
if (opt == nullptr) {
|
||||
// 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 = 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())
|
||||
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.
|
||||
// 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
|
||||
template <typename Context, typename Iterator>
|
||||
@ -1710,9 +1715,10 @@ namespace client
|
||||
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>
|
||||
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.
|
||||
// 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 {
|
||||
static void set_start_pos(Iterator &start_pos, expr &out)
|
||||
{ out.it_range = IteratorRange(start_pos, start_pos); }
|
||||
@ -1790,8 +1828,49 @@ namespace client
|
||||
if (ctx->skipping()) {
|
||||
out.reset();
|
||||
out.it_range = it_range;
|
||||
} else
|
||||
out = expr(std::string(it_range.begin() + 1, it_range.end() - 1), it_range.begin(), it_range.end());
|
||||
} else {
|
||||
// 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)
|
||||
{ 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(); }
|
||||
};
|
||||
|
||||
using skipper = ascii_char_skipper_parser;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Our macro_processor grammar
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// 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)
|
||||
{
|
||||
@ -1825,7 +1906,7 @@ namespace client
|
||||
qi::no_skip_type no_skip;
|
||||
qi::real_parser<double, strict_real_policies_without_nan_inf> strict_double;
|
||||
spirit_encoding::char_type char_;
|
||||
utf8_char_skipper_parser utf8char;
|
||||
utf8_char_parser utf8char;
|
||||
spirit::bool_type bool_;
|
||||
spirit::int_type int_;
|
||||
spirit::double_type double_;
|
||||
@ -2165,22 +2246,22 @@ namespace client
|
||||
}
|
||||
|
||||
// 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.
|
||||
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.
|
||||
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.
|
||||
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 {}
|
||||
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].
|
||||
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.
|
||||
qi::rule<Iterator, IteratorRange(), spirit_encoding::space_type> identifier;
|
||||
qi::rule<Iterator, IteratorRange(), skipper> identifier;
|
||||
// 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.
|
||||
RuleExpression logical_or_expression;
|
||||
// Logical and over relational_expressions.
|
||||
@ -2198,27 +2279,27 @@ namespace client
|
||||
// Accepting an optional parameter.
|
||||
RuleExpression optional_parameter;
|
||||
// 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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
qi::rule<Iterator, expr(const MyContext*), qi::locals<expr>, spirit_encoding::space_type> one_of;
|
||||
qi::rule<Iterator, expr(const MyContext*, const expr ¶m), spirit_encoding::space_type> one_of_list;
|
||||
qi::rule<Iterator, expr(const MyContext*), qi::locals<expr>, skipper> one_of;
|
||||
qi::rule<Iterator, expr(const MyContext*, const expr ¶m), skipper> one_of_list;
|
||||
// Evaluating the "interpolate_table" expression.
|
||||
qi::rule<Iterator, expr(const MyContext*), qi::locals<expr>, spirit_encoding::space_type> interpolate_table;
|
||||
qi::rule<Iterator, InterpolateTableContext(const MyContext*, const expr ¶m), spirit_encoding::space_type> interpolate_table_list;
|
||||
qi::rule<Iterator, expr(const MyContext*), qi::locals<expr>, skipper> interpolate_table;
|
||||
qi::rule<Iterator, InterpolateTableContext(const MyContext*, const expr ¶m), 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<OptWithPos>, spirit_encoding::space_type> assignment_statement;
|
||||
qi::rule<Iterator, std::string(const MyContext*), qi::locals<bool>, skipper> if_else_output;
|
||||
qi::rule<Iterator, std::string(const MyContext*), qi::locals<OptWithPos>, skipper> assignment_statement;
|
||||
// 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::vector<expr>(const MyContext*), spirit_encoding::space_type> initializer_list;
|
||||
qi::rule<Iterator, std::string(const MyContext*), qi::locals<bool, MyContext::NewOldVariable>, skipper> new_variable_statement;
|
||||
qi::rule<Iterator, std::vector<expr>(const MyContext*), skipper> initializer_list;
|
||||
|
||||
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)
|
||||
{
|
||||
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.back() != '\n' && context.error_message.back() != '\r')
|
||||
context.error_message += '\n';
|
||||
|
@ -57,7 +57,7 @@ void Point::rotate(double angle, const Point ¢er)
|
||||
(*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());
|
||||
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.
|
||||
// otherwise a bounding box is only defined if it has a positive area.
|
||||
template<bool IncludeBoundary>
|
||||
BoundingBox get_extents(const std::vector<Points> &pts)
|
||||
BoundingBox get_extents(const VecOfPoints &pts)
|
||||
{
|
||||
BoundingBox bbox;
|
||||
for (const Points &p : pts)
|
||||
bbox.merge(get_extents<IncludeBoundary>(p));
|
||||
return bbox;
|
||||
}
|
||||
template BoundingBox get_extents<false>(const std::vector<Points> &pts);
|
||||
template BoundingBox get_extents<true>(const std::vector<Points> &pts);
|
||||
template BoundingBox get_extents<false>(const VecOfPoints &pts);
|
||||
template BoundingBox get_extents<true>(const VecOfPoints &pts);
|
||||
|
||||
BoundingBoxf get_extents(const std::vector<Vec2d> &pts)
|
||||
{
|
||||
|
@ -9,6 +9,9 @@
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <oneapi/tbb/scalable_allocator.h>
|
||||
|
||||
|
||||
#include <Eigen/Geometry>
|
||||
|
||||
#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 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 PointConstPtrs = std::vector<const Point*>;
|
||||
using Points3 = std::vector<Vec3crd>;
|
||||
@ -57,6 +63,8 @@ using Pointfs = std::vector<Vec2d>;
|
||||
using Vec2ds = std::vector<Vec2d>;
|
||||
using Pointf3s = std::vector<Vec3d>;
|
||||
|
||||
using VecOfPoints = std::vector<Points, PointsAllocator<Points>>;
|
||||
|
||||
using Matrix2f = Eigen::Matrix<float, 2, 2, Eigen::DontAlign>;
|
||||
using Matrix2d = Eigen::Matrix<double, 2, 2, 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.
|
||||
// otherwise a bounding box is only defined if it has a positive area.
|
||||
template<bool IncludeBoundary = false>
|
||||
BoundingBox get_extents(const std::vector<Points> &pts);
|
||||
extern template BoundingBox get_extents<false>(const std::vector<Points> &pts);
|
||||
extern template BoundingBox get_extents<true>(const std::vector<Points> &pts);
|
||||
BoundingBox get_extents(const VecOfPoints &pts);
|
||||
extern template BoundingBox get_extents<false>(const VecOfPoints &pts);
|
||||
extern template BoundingBox get_extents<true>(const VecOfPoints &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.
|
||||
// The points are copied, sorted and checked for duplicates globally.
|
||||
bool has_duplicate_points(std::vector<Point> &&pts);
|
||||
inline bool has_duplicate_points(const std::vector<Point> &pts)
|
||||
bool has_duplicate_points(Points &&pts);
|
||||
inline bool has_duplicate_points(const Points &pts)
|
||||
{
|
||||
std::vector<Point> cpy = pts;
|
||||
Points cpy = pts;
|
||||
return has_duplicate_points(std::move(cpy));
|
||||
}
|
||||
|
||||
// Test for duplicate points in a vector of points.
|
||||
// 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)
|
||||
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.
|
||||
// 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());
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include "Polygon.hpp"
|
||||
#include "Polyline.hpp"
|
||||
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
double Polygon::length() const
|
||||
@ -400,14 +402,32 @@ bool has_duplicate_points(const Polygons &polys)
|
||||
{
|
||||
#if 1
|
||||
// Check globally.
|
||||
size_t cnt = 0;
|
||||
for (const Polygon &poly : polys)
|
||||
cnt += poly.points.size();
|
||||
std::vector<Point> allpts;
|
||||
allpts.reserve(cnt);
|
||||
#if 0
|
||||
// Detect duplicates by sorting with quicksort. It is quite fast, but ankerl::unordered_dense is around 1/4 faster.
|
||||
Points allpts;
|
||||
allpts.reserve(count_points(polys));
|
||||
for (const Polygon &poly : polys)
|
||||
allpts.insert(allpts.end(), poly.points.begin(), poly.points.end());
|
||||
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
|
||||
// Check per contour.
|
||||
for (const Polygon &poly : polys)
|
||||
|
@ -5,15 +5,16 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "Line.hpp"
|
||||
#include "Point.hpp"
|
||||
#include "MultiPoint.hpp"
|
||||
#include "Polyline.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Polygon;
|
||||
using Polygons = std::vector<Polygon>;
|
||||
using PolygonPtrs = std::vector<Polygon*>;
|
||||
using ConstPolygonPtrs = std::vector<const Polygon*>;
|
||||
using Polygons = std::vector<Polygon, PointsAllocator<Polygon>>;
|
||||
using PolygonPtrs = std::vector<Polygon*, PointsAllocator<Polygon*>>;
|
||||
using ConstPolygonPtrs = std::vector<const Polygon*, PointsAllocator<const Polygon*>>;
|
||||
|
||||
// Returns true if inside. Returns border_result if on boundary.
|
||||
bool contains(const Polygon& polygon, const Point& p, bool border_result = true);
|
||||
@ -241,7 +242,7 @@ inline Polylines to_polylines(Polygons &&polys)
|
||||
return polylines;
|
||||
}
|
||||
|
||||
inline Polygons to_polygons(const std::vector<Points> &paths)
|
||||
inline Polygons to_polygons(const VecOfPoints &paths)
|
||||
{
|
||||
Polygons out;
|
||||
out.reserve(paths.size());
|
||||
@ -250,7 +251,7 @@ inline Polygons to_polygons(const std::vector<Points> &paths)
|
||||
return out;
|
||||
}
|
||||
|
||||
inline Polygons to_polygons(std::vector<Points> &&paths)
|
||||
inline Polygons to_polygons(VecOfPoints &&paths)
|
||||
{
|
||||
Polygons out;
|
||||
out.reserve(paths.size());
|
||||
|
@ -17,7 +17,7 @@ namespace EdgeGrid {
|
||||
|
||||
struct TrimmedLoop
|
||||
{
|
||||
std::vector<Point> points;
|
||||
Points points;
|
||||
// Number of points per segment. Empty if the loop is
|
||||
std::vector<unsigned int> segments;
|
||||
|
||||
|
@ -158,8 +158,8 @@ inline void polylines_append(Polylines &dst, Polylines &&src)
|
||||
// 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
|
||||
// either with src piece or dst piece.
|
||||
template<typename PointType>
|
||||
inline void polylines_merge(std::vector<PointType> &dst, bool dst_first, std::vector<PointType> &&src, bool src_first)
|
||||
template<typename PointsType>
|
||||
inline void polylines_merge(PointsType &dst, bool dst_first, PointsType &&src, bool src_first)
|
||||
{
|
||||
if (dst_first) {
|
||||
if (src_first)
|
||||
|
@ -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!
|
||||
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) {
|
||||
return _u8L("The print is taller than the maximum allowed height. You might want to reduce the size of your model"
|
||||
" or change current print settings and retry.");
|
||||
return
|
||||
// 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;
|
||||
}
|
||||
|
||||
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()) {
|
||||
double width = m_config.wipe_tower_width + 2*m_wipe_tower_data.brim_width;
|
||||
|
@ -626,7 +626,7 @@ private:
|
||||
// Islands of objects and their supports extruded at the 1st layer.
|
||||
Polygons first_layer_islands() const;
|
||||
// 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.
|
||||
// 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.
|
||||
friend class GCode;
|
||||
// To allow GCodeProcessor to emit warnings.
|
||||
friend class GCodeProcessor;
|
||||
// Allow PrintObject to access m_mutex and m_cancel_callback.
|
||||
friend class PrintObject;
|
||||
};
|
||||
|
@ -2908,7 +2908,8 @@ void PrintConfigDef::init_fff_params()
|
||||
// TRN PrintSettings: "Organic supports" > "Tip Diameter"
|
||||
def->tooltip = L("Branch tip diameter for organic supports.");
|
||||
def->sidetext = L("mm");
|
||||
def->min = 0;
|
||||
def->min = 0.1f;
|
||||
def->max = 100.f;
|
||||
def->mode = comAdvanced;
|
||||
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. "
|
||||
"Branches towards the base will be thicker than this.");
|
||||
def->sidetext = L("mm");
|
||||
def->min = 0;
|
||||
def->min = 0.1f;
|
||||
def->max = 100.f;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(2));
|
||||
|
||||
@ -3867,7 +3869,9 @@ void PrintConfigDef::init_sla_params()
|
||||
def->multiline = true;
|
||||
def->full_width = true;
|
||||
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 = this->add("material_vendor", coString);
|
||||
|
@ -44,7 +44,7 @@ enum class MachineLimitsUsage {
|
||||
};
|
||||
|
||||
enum PrintHostType {
|
||||
htPrusaLink, htPrusaConnect, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS, htMainSail
|
||||
htPrusaLink, htPrusaConnect, htOctoPrint, htMainSail, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS
|
||||
};
|
||||
|
||||
enum AuthorizationType {
|
||||
|
@ -56,6 +56,20 @@
|
||||
|
||||
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
|
||||
#define SLIC3R_DEBUG
|
||||
#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());
|
||||
// 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.z() = model_object->max_z();
|
||||
m_size.z() = coord_t(model_object->max_z() * (1. / SCALING_FACTOR));
|
||||
|
||||
this->set_instances(std::move(instances));
|
||||
}
|
||||
@ -178,6 +192,7 @@ void PrintObject::make_perimeters()
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, m_layers.size() - 1),
|
||||
[this, ®ion, 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) {
|
||||
m_print->throw_if_canceled();
|
||||
LayerRegion &layerm = *m_layers[layer_idx]->get_region(region_id);
|
||||
@ -237,6 +252,7 @@ void PrintObject::make_perimeters()
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, m_layers.size()),
|
||||
[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) {
|
||||
m_print->throw_if_canceled();
|
||||
m_layers[layer_idx]->make_perimeters();
|
||||
@ -408,6 +424,7 @@ void PrintObject::infill()
|
||||
tbb::parallel_for(
|
||||
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) {
|
||||
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
|
||||
m_print->throw_if_canceled();
|
||||
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.
|
||||
tbb::blocked_range<size_t>(0, m_layers.size()),
|
||||
[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) {
|
||||
m_print->throw_if_canceled();
|
||||
m_layers[layer_idx]->make_ironing();
|
||||
@ -491,7 +509,9 @@ void PrintObject::generate_support_material()
|
||||
void PrintObject::estimate_curled_extrusions()
|
||||
{
|
||||
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";
|
||||
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)));
|
||||
// ^ 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()),
|
||||
[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) {
|
||||
std::vector<Vec3d> &out = overhangs[surface_idx];
|
||||
m_print->throw_if_canceled();
|
||||
append(out, triangulate_expolygon_3d(surfaces_w_bottom_z[surface_idx].first->expolygon,
|
||||
surfaces_w_bottom_z[surface_idx].second));
|
||||
for (Vec3d &p : out)
|
||||
p = (to_octree * p).eval();
|
||||
}
|
||||
});
|
||||
[this, &to_octree, &overhangs, &surfaces_w_bottom_z](const tbb::blocked_range<int> &range) {
|
||||
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
|
||||
for (int surface_idx = range.begin(); surface_idx < range.end(); ++surface_idx) {
|
||||
std::vector<Vec3d> &out = overhangs[surface_idx];
|
||||
m_print->throw_if_canceled();
|
||||
append(out, triangulate_expolygon_3d(surfaces_w_bottom_z[surface_idx].first->expolygon,
|
||||
surfaces_w_bottom_z[surface_idx].second));
|
||||
for (Vec3d &p : out)
|
||||
p = (to_octree * p).eval();
|
||||
}
|
||||
});
|
||||
// and gather them.
|
||||
for (size_t i = 1; i < overhangs.size(); ++ 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.
|
||||
m_layers.size()),
|
||||
[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
|
||||
// the support from the print.
|
||||
SurfaceType surface_type_bottom_other =
|
||||
@ -1057,6 +1079,7 @@ void PrintObject::detect_surfaces_type()
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, m_layers.size()),
|
||||
[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) {
|
||||
m_print->throw_if_canceled();
|
||||
LayerRegion *layerm = m_layers[idx_layer]->m_regions[region_id];
|
||||
@ -1115,6 +1138,7 @@ void PrintObject::process_external_surfaces()
|
||||
tbb::parallel_for(
|
||||
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) {
|
||||
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx)
|
||||
if (layer_expansions_and_voids[layer_idx + 1]) {
|
||||
// Layer above is partially filled with solid infill (top, bottom, bridging...),
|
||||
@ -1140,6 +1164,7 @@ void PrintObject::process_external_surfaces()
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, m_layers.size()),
|
||||
[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) {
|
||||
m_print->throw_if_canceled();
|
||||
// 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;
|
||||
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());
|
||||
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;
|
||||
@ -1199,24 +1211,13 @@ void PrintObject::discover_vertical_shells()
|
||||
if (top_bottom_surfaces_all_regions) {
|
||||
// This is a multi-material print and interface_shells are disabled, meaning that the vertical shell thickness
|
||||
// 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";
|
||||
//FIXME Improve the heuristics for a grain size.
|
||||
size_t grain_size = std::max(num_layers / 16, size_t(1));
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, num_layers, grain_size),
|
||||
[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 size_t num_regions = this->num_printing_regions();
|
||||
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) {
|
||||
const PrintRegion ®ion = 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.
|
||||
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::blocked_range<size_t>(0, num_layers, grain_size),
|
||||
[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 };
|
||||
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
|
||||
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";
|
||||
grain_size = 1;
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, num_layers, grain_size),
|
||||
[this, region_id, &cache_top_botom_regions]
|
||||
(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());
|
||||
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
|
||||
m_print->throw_if_canceled();
|
||||
@ -1395,13 +1394,25 @@ void PrintObject::discover_vertical_shells()
|
||||
coordf_t print_z = layer->print_z;
|
||||
int i = int(idx_layer) + 1;
|
||||
int itop = int(idx_layer) + n_top_layers;
|
||||
bool at_least_one_top_projected = false;
|
||||
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) {
|
||||
at_least_one_top_projected = true;
|
||||
const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i];
|
||||
combine_holes(cache.holes);
|
||||
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 (i < int(cache_top_botom_regions.size()) &&
|
||||
(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();
|
||||
int i = int(idx_layer) - 1;
|
||||
int ibottom = int(idx_layer) - n_bottom_layers;
|
||||
bool at_least_one_bottom_projected = false;
|
||||
for (; i >= 0 &&
|
||||
(i > ibottom || bottom_z - m_layers[i]->bottom_z() < region_config.bottom_solid_min_thickness - EPSILON);
|
||||
-- i) {
|
||||
at_least_one_bottom_projected = true;
|
||||
const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i];
|
||||
combine_holes(cache.holes);
|
||||
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 (i >= 0 &&
|
||||
(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.
|
||||
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
|
||||
regularized_shell.erase(std::remove_if(regularized_shell.begin(), regularized_shell.end(),
|
||||
[&min_perimeter_infill_spacing](const ExPolygon &p) {
|
||||
return p.area() < min_perimeter_infill_spacing * scaled(8.0);
|
||||
[&min_perimeter_infill_spacing, &internal_volume](const ExPolygon &p) {
|
||||
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());
|
||||
}
|
||||
@ -1600,21 +1632,18 @@ void PrintObject::bridge_over_infill()
|
||||
int layer_index,
|
||||
Polygons new_polys,
|
||||
const LayerRegion *region,
|
||||
double bridge_angle,
|
||||
bool supported_by_lightning)
|
||||
double bridge_angle)
|
||||
: original_surface(original_surface)
|
||||
, layer_index(layer_index)
|
||||
, new_polys(new_polys)
|
||||
, region(region)
|
||||
, bridge_angle(bridge_angle)
|
||||
, supported_by_lightning(supported_by_lightning)
|
||||
{}
|
||||
const Surface *original_surface;
|
||||
int layer_index;
|
||||
Polygons new_polys;
|
||||
const LayerRegion *region;
|
||||
double bridge_angle;
|
||||
bool supported_by_lightning;
|
||||
};
|
||||
|
||||
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::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) {
|
||||
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
|
||||
for (size_t lidx = r.begin(); lidx < r.end(); lidx++) {
|
||||
const Layer *layer = po->get_layer(lidx);
|
||||
if (layer->lower_layer == nullptr) {
|
||||
@ -1674,7 +1704,7 @@ void PrintObject::bridge_over_infill()
|
||||
}
|
||||
}
|
||||
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
|
||||
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),
|
||||
&layers_to_generate_infill,
|
||||
&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++) {
|
||||
size_t lidx = layers_to_generate_infill[job_idx];
|
||||
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
|
||||
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::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,
|
||||
&layer_area_covered_by_candidates](
|
||||
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++) {
|
||||
size_t lidx = layers_with_candidates[job_idx];
|
||||
for (const auto &candidate : surfaces_by_layer.at(lidx)) {
|
||||
@ -1767,7 +1799,7 @@ void PrintObject::bridge_over_infill()
|
||||
if (clustered_layers_for_threads.empty() ||
|
||||
this->get_layer(clustered_layers_for_threads.back().back())->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 ||
|
||||
intersection(layer_area_covered_by_candidates[clustered_layers_for_threads.back().back()],
|
||||
layer_area_covered_by_candidates[pair.first])
|
||||
@ -1797,9 +1829,9 @@ void PrintObject::bridge_over_infill()
|
||||
ExPolygons not_sparse_infill{};
|
||||
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) {
|
||||
// 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);
|
||||
if (layer->print_z < bottom_z)
|
||||
if (layer->print_z < bottom_z && i < int(lidx) - 1)
|
||||
break;
|
||||
|
||||
for (const LayerRegion *region : layer->regions()) {
|
||||
@ -1990,8 +2022,8 @@ void PrintObject::bridge_over_infill()
|
||||
// reconstruct polygon from polygon sections
|
||||
struct TracedPoly
|
||||
{
|
||||
std::vector<Point> lows;
|
||||
std::vector<Point> highs;
|
||||
Points lows;
|
||||
Points highs;
|
||||
};
|
||||
|
||||
std::vector<TracedPoly> current_traced_polys;
|
||||
@ -2070,6 +2102,7 @@ void PrintObject::bridge_over_infill()
|
||||
determine_bridging_angle,
|
||||
construct_anchored_polygon](
|
||||
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 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];
|
||||
@ -2102,9 +2135,10 @@ void PrintObject::bridge_over_infill()
|
||||
}
|
||||
|
||||
// Gather deep infill areas, where thick bridges fit
|
||||
coordf_t spacing = surfaces_by_layer[lidx].front().region->flow(frSolidInfill, true).scaled_spacing();
|
||||
coordf_t target_flow_height = surfaces_by_layer[lidx].front().region->flow(frSolidInfill, true).height() * target_flow_height_factor;
|
||||
Polygons deep_infill_area = gather_areas_w_depth(po, lidx, target_flow_height);
|
||||
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->bridging_flow(frSolidInfill, true).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
|
||||
@ -2131,6 +2165,7 @@ void PrintObject::bridge_over_infill()
|
||||
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
|
||||
Polygons lightning_area;
|
||||
Polygons expansion_area;
|
||||
Polygons total_fill_area;
|
||||
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());
|
||||
Polygons fill_polys = to_polygons(region->fill_expolygons());
|
||||
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);
|
||||
expansion_area = closing(expansion_area, SCALED_EPSILON);
|
||||
expansion_area = intersection(expansion_area, deep_infill_area);
|
||||
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
|
||||
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));
|
||||
#endif
|
||||
|
||||
|
||||
std::vector<CandidateSurface> expanded_surfaces;
|
||||
expanded_surfaces.reserve(surfaces_by_layer[lidx].size());
|
||||
for (const CandidateSurface &candidate : surfaces_by_layer[lidx]) {
|
||||
const Flow &flow = candidate.region->bridging_flow(frSolidInfill, true);
|
||||
Polygons area_to_be_bridge = expand(candidate.new_polys, flow.scaled_spacing());
|
||||
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())
|
||||
continue;
|
||||
@ -2185,7 +2231,7 @@ void PrintObject::bridge_over_infill()
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
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
|
||||
|
||||
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);
|
||||
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();
|
||||
|
||||
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++) {
|
||||
if (surfaces_by_layer.find(lidx) == surfaces_by_layer.end())
|
||||
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) {
|
||||
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;
|
||||
|
||||
// Transform the triangle into worlds coords.
|
||||
|
@ -43,7 +43,8 @@ Point ConcaveHull::centroid(const Points &pp)
|
||||
Points ConcaveHull::calculate_centroids() const
|
||||
{
|
||||
// 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::back_inserter(centroids),
|
||||
[](const Polygon &poly) { return centroid(poly); });
|
||||
|
@ -6,6 +6,8 @@
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
|
||||
#include <libslic3r/Point.hpp>
|
||||
|
||||
struct indexed_triangle_set;
|
||||
|
||||
namespace Slic3r {
|
||||
@ -13,7 +15,7 @@ namespace Slic3r {
|
||||
class ExPolygon;
|
||||
class Polygon;
|
||||
using ExPolygons = std::vector<ExPolygon>;
|
||||
using Polygons = std::vector<Polygon>;
|
||||
using Polygons = std::vector<Polygon, PointsAllocator<Polygon>>;
|
||||
|
||||
namespace sla {
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include <libslic3r/Polygon.hpp>
|
||||
#include <libslic3r/ExPolygon.hpp>
|
||||
#include <libslic3r/AABBMesh.hpp>
|
||||
|
||||
@ -14,9 +15,6 @@
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
using Polygons = std::vector<Polygon>;
|
||||
using ExPolygons = std::vector<ExPolygon>;
|
||||
|
||||
namespace sla {
|
||||
|
||||
struct SupportTreeConfig
|
||||
|
@ -6,6 +6,8 @@
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
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
|
||||
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<Vec3i> final_indices;
|
||||
final_indices.reserve(face_indices.size());
|
||||
|
@ -8,10 +8,13 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace ClipperLib { class PolyNode; }
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace ClipperLib {
|
||||
class PolyNode;
|
||||
using PolyNodes = std::vector<PolyNode*, PointsAllocator<PolyNode*>>;
|
||||
}
|
||||
|
||||
class 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);
|
||||
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.
|
||||
// Returns pairs of PrintObject idx and instance of that PrintObject.
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "PrintBase.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
#include "Tesselate.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "libslic3r.h"
|
||||
#include "tbb/parallel_for.h"
|
||||
#include "tbb/blocked_range.h"
|
||||
@ -24,6 +25,8 @@
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <math.h>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
@ -206,18 +209,38 @@ std::vector<ExtrusionLine> to_short_lines(const ExtrusionEntity *e, float length
|
||||
}
|
||||
|
||||
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;
|
||||
if (fabs(point.distance) < 1.5 * flow_width) {
|
||||
curled_up_height = 0.85 * prev_line_curled_height;
|
||||
float curled_up_height = 0;
|
||||
if (fabs(distance) < 3.0 * flow_width) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -312,9 +335,9 @@ std::vector<ExtrusionLine> check_extrusion_entity_stability(const ExtrusionEntit
|
||||
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};
|
||||
|
||||
const ExtrusionLine nearest_prev_layer_line = prev_layer_lines.get_lines().size() > 0 ?
|
||||
prev_layer_lines.get_line(curr_point.nearest_prev_layer_line) :
|
||||
ExtrusionLine{};
|
||||
Vec2f middle = 0.5 * (line_out.a + line_out.b);
|
||||
auto [middle_distance, bottom_line_idx, x] = prev_layer_lines.distance_from_lines_extra<false>(middle);
|
||||
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
|
||||
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) {
|
||||
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) {
|
||||
line_out.support_point_generated = potential_cause;
|
||||
line_out.form_quality = 0.5f;
|
||||
@ -349,8 +372,9 @@ std::vector<ExtrusionLine> check_extrusion_entity_stability(const ExtrusionEntit
|
||||
bridged_distance = 0.0f;
|
||||
}
|
||||
|
||||
line_out.curled_up_height = estimate_curled_up_height(curr_point, layer_region->layer()->height, flow_width,
|
||||
nearest_prev_layer_line.curled_up_height, params);
|
||||
line_out.curled_up_height = estimate_curled_up_height(middle_distance, 0.5 * (prev_point.curvature + curr_point.curvature),
|
||||
layer_region->layer()->height, flow_width, bottom_line.curled_up_height,
|
||||
params);
|
||||
|
||||
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{};
|
||||
|
||||
for (SupportLayer *l : layers) {
|
||||
l->malformed_lines.clear();
|
||||
l->curled_lines.clear();
|
||||
std::vector<ExtrusionLine> current_layer_lines;
|
||||
|
||||
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);
|
||||
|
||||
for (size_t i = 0; i < annotated_points.size(); ++i) {
|
||||
ExtendedPoint &curr_point = annotated_points[i];
|
||||
float line_len = i > 0 ? ((annotated_points[i - 1].position - curr_point.position).norm()) : 0.0f;
|
||||
ExtrusionLine line_out{i > 0 ? annotated_points[i - 1].position.cast<float>() : curr_point.position.cast<float>(),
|
||||
curr_point.position.cast<float>(), line_len, extrusion};
|
||||
const ExtendedPoint &a = i > 0 ? annotated_points[i - 1] : annotated_points[i];
|
||||
const ExtendedPoint &b = annotated_points[i];
|
||||
ExtrusionLine line_out{a.position.cast<float>(), b.position.cast<float>(), float((a.position - b.position).norm()),
|
||||
extrusion};
|
||||
|
||||
const ExtrusionLine nearest_prev_layer_line = prev_layer_lines.get_lines().size() > 0 ?
|
||||
prev_layer_lines.get_line(curr_point.nearest_prev_layer_line) :
|
||||
ExtrusionLine{};
|
||||
Vec2f middle = 0.5 * (line_out.a + line_out.b);
|
||||
auto [middle_distance, bottom_line_idx, x] = prev_layer_lines.distance_from_lines_extra<false>(middle);
|
||||
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 v2 = (curr_point.position.cast<float>() - nearest_prev_layer_line.a);
|
||||
auto d = (v1.x() * v2.y()) - (v1.y() * v2.x());
|
||||
if (d > 0) {
|
||||
curr_point.distance *= -1.0f;
|
||||
}
|
||||
Vec2f v1 = (bottom_line.b - bottom_line.a);
|
||||
Vec2f v2 = (a.position.cast<float>() - bottom_line.a);
|
||||
auto d = (v1.x() * v2.y()) - (v1.y() * v2.x());
|
||||
float sign = (d > 0) ? -1.0f : 1.0f;
|
||||
|
||||
line_out.curled_up_height = estimate_curled_up_height(curr_point, l->height, flow_width,
|
||||
nearest_prev_layer_line.curled_up_height, params);
|
||||
line_out.curled_up_height = estimate_curled_up_height(middle_distance * sign, 0.5 * (a.curvature + b.curvature), l->height,
|
||||
flow_width, bottom_line.curled_up_height, params);
|
||||
|
||||
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) {
|
||||
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 ¶ms)
|
||||
{
|
||||
#ifdef DEBUG_FILES
|
||||
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
|
||||
|
||||
LD prev_layer_lines{};
|
||||
|
||||
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>();
|
||||
AABBTreeLines::LinesDistancer<Linef> prev_layer_boundary{std::move(boundary_lines)};
|
||||
std::vector<ExtrusionLine> current_layer_lines;
|
||||
for (const LayerRegion *layer_region : l->regions()) {
|
||||
for (const ExtrusionEntity *extrusion : layer_region->perimeters().flatten().entities) {
|
||||
|
||||
if (!extrusion->role().is_external_perimeter()) continue;
|
||||
if (!extrusion->role().is_external_perimeter())
|
||||
continue;
|
||||
|
||||
Points extrusion_pts;
|
||||
extrusion->collect_points(extrusion_pts);
|
||||
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,
|
||||
params.bridge_distance);
|
||||
auto annotated_points = estimate_points_properties<true, true, false, false>(extrusion_pts, prev_layer_lines, flow_width,
|
||||
params.bridge_distance);
|
||||
for (size_t i = 0; i < annotated_points.size(); ++i) {
|
||||
ExtendedPoint &curr_point = annotated_points[i];
|
||||
float line_len = i > 0 ? ((annotated_points[i - 1].position - curr_point.position).norm()) : 0.0f;
|
||||
ExtrusionLine line_out{i > 0 ? annotated_points[i - 1].position.cast<float>() : curr_point.position.cast<float>(),
|
||||
curr_point.position.cast<float>(), line_len, extrusion};
|
||||
const ExtendedPoint &a = i > 0 ? annotated_points[i - 1] : annotated_points[i];
|
||||
const ExtendedPoint &b = annotated_points[i];
|
||||
ExtrusionLine line_out{a.position.cast<float>(), b.position.cast<float>(), float((a.position - b.position).norm()),
|
||||
extrusion};
|
||||
|
||||
const ExtrusionLine nearest_prev_layer_line = prev_layer_lines.get_lines().size() > 0 ?
|
||||
prev_layer_lines.get_line(curr_point.nearest_prev_layer_line) :
|
||||
ExtrusionLine{};
|
||||
Vec2f middle = 0.5 * (line_out.a + line_out.b);
|
||||
auto [middle_distance, bottom_line_idx, x] = prev_layer_lines.distance_from_lines_extra<false>(middle);
|
||||
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 :
|
||||
1.0f;
|
||||
curr_point.distance *= sign;
|
||||
// correctify the distance sign using slice polygons
|
||||
float sign = (prev_layer_boundary.distance_from_lines<true>(middle.cast<double>()) + 0.5f * flow_width) < 0.0f ? -1.0f : 1.0f;
|
||||
|
||||
line_out.curled_up_height = estimate_curled_up_height(curr_point, layer_region->layer()->height, flow_width,
|
||||
nearest_prev_layer_line.curled_up_height, params);
|
||||
line_out.curled_up_height = estimate_curled_up_height(middle_distance * sign, 0.5 * (a.curvature + b.curvature),
|
||||
l->height, flow_width, bottom_line.curled_up_height, params);
|
||||
|
||||
current_layer_lines.push_back(line_out);
|
||||
}
|
||||
@ -1153,7 +1176,7 @@ void estimate_malformations(LayerPtrs &layers, const Params ¶ms)
|
||||
|
||||
for (const ExtrusionLine &line : current_layer_lines) {
|
||||
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 ¶ms)
|
||||
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) {
|
||||
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]);
|
||||
for (const ExtrusionLine &line : current_layer_lines) {
|
||||
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]);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -42,7 +42,7 @@ struct Params
|
||||
BrimType brim_type;
|
||||
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 curling_tolerance_limit = 0.1f;
|
||||
|
||||
|
@ -58,8 +58,6 @@
|
||||
|
||||
// Enable alternative version of file_wildcards()
|
||||
#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_
|
||||
|
@ -10,3 +10,12 @@ Slic3r::Timer::~Timer()
|
||||
BOOST_LOG_TRIVIAL(debug) << "Timer '" << m_name << "' spend " <<
|
||||
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";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,5 +27,66 @@ public:
|
||||
~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
|
||||
#endif // libslic3r_Timer_hpp_
|
||||
|
||||
#endif // libslic3r_Timer_hpp_
|
||||
|
@ -257,6 +257,8 @@ void TreeModelVolumes::precalculate(const PrintObject& print_object, const coord
|
||||
auto it = radius_until_layer.find(r);
|
||||
if (it == radius_until_layer.end())
|
||||
radius_until_layer.emplace_hint(it, r, current_layer);
|
||||
else
|
||||
assert(it->second >= current_layer);
|
||||
};
|
||||
// regular radius
|
||||
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!";
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -512,7 +512,7 @@ private:
|
||||
*/
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
calculateWallRestrictions(std::vector<RadiusLayerPair>{ RadiusLayerPair(key) }, {});
|
||||
calculateWallRestrictions(std::vector<RadiusLayerPair>{ RadiusLayerPair(key) }, []{});
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -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()),
|
||||
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
|
||||
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),
|
||||
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_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),
|
||||
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)),
|
||||
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_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)),
|
||||
@ -96,7 +97,7 @@ TreeSupportSettings::TreeSupportSettings(const TreeSupportMeshGroupSettings& mes
|
||||
settings(mesh_group_settings),
|
||||
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) {
|
||||
// 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 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;
|
||||
//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
|
||||
// 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.
|
||||
// The 2*z_distance_delta is only a catch for when the support angle is very high.
|
||||
// Used only if not min_xy_dist.
|
||||
const coord_t max_overhang_insert_lag = config.z_distance_top_layers > 0 ?
|
||||
std::max<coord_t>(round_up_divide(config.xy_distance, max_overhang_speed / 2), 2 * config.z_distance_top_layers) :
|
||||
0;
|
||||
coord_t max_overhang_insert_lag = 0;
|
||||
if (config.z_distance_top_layers > 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;
|
||||
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);
|
||||
|
||||
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,
|
||||
// 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);
|
||||
@ -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)) {
|
||||
// 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))
|
||||
extra_speed += config.branch_radius * config.diameter_scale_bp_radius;
|
||||
extra_speed += config.bp_radius_increase_per_layer;
|
||||
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));
|
||||
}
|
||||
|
||||
@ -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.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));
|
||||
// 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.
|
||||
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.
|
||||
@ -3708,12 +3713,13 @@ static std::pair<int, int> discretize_circle(const Vec3f ¢er, const Vec3f &n
|
||||
return { begin, int(pts.size()) };
|
||||
}
|
||||
|
||||
static void extrude_branch(
|
||||
const std::vector<SupportElement*> &path,
|
||||
const TreeSupportSettings &config,
|
||||
const SlicingParameters &slicing_params,
|
||||
const std::vector<SupportElements> &move_bounds,
|
||||
indexed_triangle_set &result)
|
||||
// Returns Z span of the generated mesh.
|
||||
static std::pair<float, float> extrude_branch(
|
||||
const std::vector<const SupportElement*> &path,
|
||||
const TreeSupportSettings &config,
|
||||
const SlicingParameters &slicing_params,
|
||||
const std::vector<SupportElements> &move_bounds,
|
||||
indexed_triangle_set &result)
|
||||
{
|
||||
Vec3d p1, p2, p3;
|
||||
Vec3d v1, v2;
|
||||
@ -3726,6 +3732,8 @@ static void extrude_branch(
|
||||
// char fname[2048];
|
||||
// static int irun = 0;
|
||||
|
||||
float zmin, zmax;
|
||||
|
||||
for (size_t ipath = 1; ipath < path.size(); ++ ipath) {
|
||||
const SupportElement &prev = *path[ipath - 1];
|
||||
const SupportElement ¤t = *path[ipath];
|
||||
@ -3742,6 +3750,7 @@ static void extrude_branch(
|
||||
angle_step = M_PI / (2. * nsteps);
|
||||
int ifan = int(result.vertices.size());
|
||||
result.vertices.emplace_back((p1 - nprev * radius).cast<float>());
|
||||
zmin = result.vertices.back().z();
|
||||
float 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);
|
||||
@ -3772,6 +3781,7 @@ static void extrude_branch(
|
||||
}
|
||||
int ifan = int(result.vertices.size());
|
||||
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);
|
||||
// sprintf(fname, "d:\\temp\\meshes\\tree-partial-%d.obj", ++ irun);
|
||||
// its_write_obj(result, fname);
|
||||
@ -3799,6 +3809,8 @@ static void extrude_branch(
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return std::make_pair(zmin, zmax);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -4120,15 +4132,13 @@ static void organic_smooth_branches_avoid_collisions(
|
||||
#endif // TREE_SUPPORT_ORGANIC_NUDGE_NEW
|
||||
|
||||
// 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,
|
||||
const TreeModelVolumes &volumes,
|
||||
TreeModelVolumes &volumes,
|
||||
const TreeSupportSettings &config,
|
||||
std::vector<SupportElements> &move_bounds,
|
||||
std::function<void()> throw_on_cancel)
|
||||
{
|
||||
static int irun = 0;
|
||||
|
||||
// All SupportElements are put into a layer independent storage to improve parallelization.
|
||||
std::vector<std::pair<SupportElement*, int>> elements_with_link_down;
|
||||
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);
|
||||
|
||||
// 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.
|
||||
for (SupportElements &elements : move_bounds)
|
||||
for (SupportElement &element : elements)
|
||||
element.state.marked = false;
|
||||
|
||||
// Traverse all nodes, generate tubes.
|
||||
// Traversal stack with nodes and thier 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];
|
||||
// Traversal stack with nodes and their current parent
|
||||
|
||||
for (SupportElement &start_element : layer)
|
||||
if (! start_element.state.marked && ! start_element.parents.empty()) {
|
||||
// Collect elements up to a bifurcation above.
|
||||
start_element.state.marked = true;
|
||||
for (size_t parent_idx = 0; parent_idx < start_element.parents.size(); ++ parent_idx) {
|
||||
path.clear();
|
||||
path.emplace_back(&start_element);
|
||||
// Traverse each branch until it branches again.
|
||||
SupportElement &first_parent = layer_above[start_element.parents[parent_idx]];
|
||||
assert(path.back()->state.layer_idx + 1 == first_parent.state.layer_idx);
|
||||
path.emplace_back(&first_parent);
|
||||
if (first_parent.parents.size() < 2)
|
||||
first_parent.state.marked = true;
|
||||
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(path.back()->state.layer_idx + 1 == next_parent.state.layer_idx);
|
||||
path.emplace_back(&next_parent);
|
||||
if (next_parent.parents.size() > 1)
|
||||
break;
|
||||
next_parent.state.marked = true;
|
||||
if (next_parent.parents.size() == 0)
|
||||
break;
|
||||
parent = &next_parent;
|
||||
struct Branch {
|
||||
std::vector<const SupportElement*> path;
|
||||
bool has_root{ false };
|
||||
bool has_tip { false };
|
||||
};
|
||||
|
||||
struct Slice {
|
||||
Polygons polygons;
|
||||
size_t num_branches{ 0 };
|
||||
};
|
||||
|
||||
struct Tree {
|
||||
std::vector<Branch> branches;
|
||||
|
||||
std::vector<Slice> slices;
|
||||
LayerIndex first_layer_id{ -1 };
|
||||
};
|
||||
|
||||
std::vector<Tree> trees;
|
||||
|
||||
struct TreeVisitor {
|
||||
static void visit_recursive(std::vector<SupportElements> &move_bounds, SupportElement &start_element, Tree &out) {
|
||||
assert(! start_element.state.marked && ! start_element.parents.empty());
|
||||
// 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.
|
||||
partial_mesh.clear();
|
||||
extrude_branch(path, config, slicing_params, move_bounds, partial_mesh);
|
||||
#if 0
|
||||
{
|
||||
char fname[2048];
|
||||
static int irun = 0;
|
||||
sprintf(fname, "d:\\temp\\meshes\\tree-raw-%d.obj", ++ irun);
|
||||
its_write_obj(partial_mesh, fname);
|
||||
#if 0
|
||||
temp_mesh.clear();
|
||||
cut_mesh(partial_mesh, layer_z(slicing_params, path.back()->state.layer_idx) + EPSILON, nullptr, &temp_mesh, false);
|
||||
sprintf(fname, "d:\\temp\\meshes\\tree-trimmed1-%d.obj", irun);
|
||||
its_write_obj(temp_mesh, fname);
|
||||
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);
|
||||
std::pair<float, float> zspan = extrude_branch(branch.path, config, slicing_params, move_bounds, partial_mesh);
|
||||
LayerIndex layer_begin = branch.has_root ?
|
||||
branch.path.front()->state.layer_idx :
|
||||
std::min(branch.path.front()->state.layer_idx, layer_idx_ceil(slicing_params, config, zspan.first));
|
||||
LayerIndex layer_end = (branch.has_tip ?
|
||||
branch.path.back()->state.layer_idx :
|
||||
std::max(branch.path.back()->state.layer_idx, layer_idx_floor(slicing_params, config, zspan.second))) + 1;
|
||||
slice_z.clear();
|
||||
for (LayerIndex layer_idx = layer_begin; layer_idx < layer_end; ++ layer_idx) {
|
||||
const double print_z = layer_z(slicing_params, config, layer_idx);
|
||||
const double bottom_z = layer_idx > 0 ? layer_z(slicing_params, config, layer_idx - 1) : 0.;
|
||||
slice_z.emplace_back(float(0.5 * (bottom_z + print_z)));
|
||||
}
|
||||
#endif
|
||||
its_merge(cummulative_mesh, partial_mesh);
|
||||
std::vector<Polygons> slices = slice_mesh(partial_mesh, slice_z, mesh_slicing_params, throw_on_cancel);
|
||||
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());
|
||||
finalize_interface_and_support_areas(print_object, volumes, config, overhangs, support_layer_storage, support_roof_storage,
|
||||
bottom_contacts, top_contacts, intermediate_layers, layer_storage, throw_on_cancel);
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, trees.size()),
|
||||
[&trees, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
|
||||
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);
|
||||
else {
|
||||
assert(print_object.config().support_material_style == smsOrganic);
|
||||
indexed_triangle_set branches = draw_branches(*print.get_object(processing.second.front()), volumes, config, move_bounds, throw_on_cancel);
|
||||
// Reduce memory footprint. After this point only slice_branches() will use volumes and from that only collisions with zero radius will be used.
|
||||
volumes.clear_all_but_object_collision();
|
||||
slice_branches(*print.get_object(processing.second.front()), volumes, config, overhangs, move_bounds, branches,
|
||||
std::vector<Polygons> support_layer_storage = draw_branches(*print.get_object(processing.second.front()), volumes, config, move_bounds, throw_on_cancel);
|
||||
std::vector<Polygons> support_roof_storage(support_layer_storage.size());
|
||||
finalize_interface_and_support_areas(print_object, volumes, config, overhangs, support_layer_storage, support_roof_storage,
|
||||
bottom_contacts, top_contacts, intermediate_layers, layer_storage, throw_on_cancel);
|
||||
}
|
||||
|
||||
|
@ -302,9 +302,9 @@ public:
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
@ -330,17 +330,18 @@ public:
|
||||
*/
|
||||
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;
|
||||
/*!
|
||||
* \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-
|
||||
*/
|
||||
@ -418,7 +419,9 @@ public:
|
||||
public:
|
||||
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.
|
||||
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.
|
||||
@ -470,9 +473,9 @@ public:
|
||||
{
|
||||
return (distance_to_top <= tip_layers ? min_radius + (branch_radius - min_radius) * distance_to_top / tip_layers : // tip
|
||||
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
|
||||
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
|
||||
{
|
||||
double scale = (layer_start_bp_radius - int(layer_idx)) * diameter_scale_bp_radius;
|
||||
return scale > 0 ? branch_radius + branch_radius * scale : 0;
|
||||
double num_layers_widened = layer_start_bp_radius - layer_idx;
|
||||
return num_layers_widened > 0 ? branch_radius + num_layers_widened * bp_radius_increase_per_layer : 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -1911,6 +1911,26 @@ std::vector<ExPolygons> slice_mesh_ex(
|
||||
this_mode == MeshSlicingParams::SlicingMode::EvenOdd ? ClipperLib::pftEvenOdd :
|
||||
this_mode == MeshSlicingParams::SlicingMode::PositiveLargestContour ? ClipperLib::pftPositive : ClipperLib::pftNonZero,
|
||||
&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
|
||||
if (this_mode == MeshSlicingParams::SlicingMode::PositiveLargestContour)
|
||||
keep_largest_contour_only(expolygons);
|
||||
@ -1921,6 +1941,16 @@ std::vector<ExPolygons> slice_mesh_ex(
|
||||
append(simplified, ex.simplify(resolution));
|
||||
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";
|
||||
|
@ -106,8 +106,8 @@ enum Axis {
|
||||
NUM_AXES_WITH_UNKNOWN,
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline void append(std::vector<T>& dest, const std::vector<T>& src)
|
||||
template <typename T, typename Alloc, typename Alloc2>
|
||||
inline void append(std::vector<T, Alloc> &dest, const std::vector<T, Alloc2> &src)
|
||||
{
|
||||
if (dest.empty())
|
||||
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());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void append(std::vector<T>& dest, std::vector<T>&& src)
|
||||
template <typename T, typename Alloc>
|
||||
inline void append(std::vector<T, Alloc> &dest, std::vector<T, Alloc> &&src)
|
||||
{
|
||||
if (dest.empty())
|
||||
dest = std::move(src);
|
||||
|
@ -250,6 +250,8 @@ set(SLIC3R_GUI_SOURCES
|
||||
Utils/Http.hpp
|
||||
Utils/FixModelByWin10.cpp
|
||||
Utils/FixModelByWin10.hpp
|
||||
Utils/Mainsail.cpp
|
||||
Utils/Mainsail.hpp
|
||||
Utils/OctoPrint.cpp
|
||||
Utils/OctoPrint.hpp
|
||||
Utils/Duet.cpp
|
||||
|
@ -593,7 +593,7 @@ void GLVolumeCollection::load_object_auxiliary(
|
||||
return;
|
||||
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) {
|
||||
if (mesh.empty())
|
||||
return;
|
||||
|
@ -3171,6 +3171,8 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
|
||||
}
|
||||
else {
|
||||
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);
|
||||
};
|
||||
bool is_filaments_changed = changed(AppConfig::SECTION_FILAMENTS);
|
||||
|
@ -697,7 +697,9 @@ void GCodeViewer::init()
|
||||
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Line;
|
||||
buffer.vertices.format = VBuffer::EFormat::Position;
|
||||
#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
|
||||
buffer.shader = "flat";
|
||||
#endif // ENABLE_GL_CORE_PROFILE
|
||||
@ -836,7 +838,8 @@ void GCodeViewer::refresh(const GCodeProcessorResult& gcode_result, const std::v
|
||||
case EMoveType::Extrude:
|
||||
{
|
||||
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.temperature.update_from(curr.temperature);
|
||||
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
|
||||
if (show_estimated_time) {
|
||||
ImGui::Spacing();
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user