Merge branch 'master' of https://github.com/Prusa-Development/PrusaSlicerPrivate into et_outofbed_optimization

This commit is contained in:
enricoturri1966 2023-05-11 11:40:50 +02:00
commit 653d7bc678
153 changed files with 6993 additions and 4737 deletions

View File

@ -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)
@ -545,6 +546,7 @@ foreach(po_file ${L10N_PO_FILES})
endforeach()
find_package(NLopt 1.4 REQUIRED)
slic3r_remap_configs(NLopt::nlopt RelWithDebInfo Release)
if(SLIC3R_STATIC)
set(OPENVDB_USE_STATIC_LIBS ON)

View File

@ -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.

View File

@ -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

View File

@ -1,4 +1,5 @@
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.

View File

@ -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.2
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
@ -310,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*
@ -658,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.
@ -1022,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
@ -1094,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
@ -1182,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*
@ -1263,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*
@ -1336,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*
@ -1393,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
@ -1429,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
@ -1441,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

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 KiB

Binary file not shown.

View File

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

View File

@ -73,25 +73,6 @@ static int const Skip = -2; //edge that would otherwise close a path
#define TOLERANCE (1.0e-20)
#define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE))
// Output polygon.
struct OutRec {
int Idx;
bool IsHole;
bool IsOpen;
//The 'FirstLeft' field points to another OutRec that contains or is the
//'parent' of OutRec. It is 'first left' because the ActiveEdgeList (AEL) is
//parsed left from the current edge (owning OutRec) until the owner OutRec
//is found. This field simplifies sorting the polygons into a tree structure
//which reflects the parent/child relationships of all polygons.
//This field should be renamed Parent, and will be later.
OutRec *FirstLeft;
// Used only by void Clipper::BuildResult2(PolyTree& polytree)
PolyNode *PolyNd;
// Linked list of output points, dynamically allocated.
OutPt *Pts;
OutPt *BottomPt;
};
//------------------------------------------------------------------------------
inline IntPoint IntPoint2d(cInt x, cInt y)
@ -131,7 +112,7 @@ int PolyTree::Total() const
void PolyNode::AddChild(PolyNode& child)
{
unsigned cnt = (unsigned)Childs.size();
Childs.push_back(&child);
Childs.emplace_back(&child);
child.Parent = this;
child.Index = cnt;
}
@ -693,7 +674,7 @@ TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward)
locMin.RightBound = E;
E->WindDelta = 0;
Result = ProcessBound(E, NextIsForward);
m_MinimaList.push_back(locMin);
m_MinimaList.emplace_back(locMin);
}
return Result;
}
@ -784,7 +765,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)
@ -915,7 +896,7 @@ bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, b
E->NextInLML = E->Next;
E = E->Next;
}
m_MinimaList.push_back(locMin);
m_MinimaList.emplace_back(locMin);
return true;
}
@ -968,7 +949,7 @@ bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, b
locMin.LeftBound = 0;
else if (locMin.RightBound->OutIdx == Skip)
locMin.RightBound = 0;
m_MinimaList.push_back(locMin);
m_MinimaList.emplace_back(locMin);
if (!leftBoundIsForward) E = E2;
}
return true;
@ -1061,8 +1042,7 @@ IntRect ClipperBase::GetBounds()
Clipper::Clipper(int initOptions) :
ClipperBase(),
m_OutPtsFree(nullptr),
m_OutPtsChunkSize(32),
m_OutPtsChunkLast(32),
m_OutPtsChunkLast(m_OutPtsChunkSize),
m_ActiveEdges(nullptr),
m_SortedEdges(nullptr)
{
@ -1079,7 +1059,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;
@ -1153,23 +1133,23 @@ bool Clipper::ExecuteInternal()
//FIXME Vojtech: Does it not invalidate the loop hierarchy maintained as OutRec::FirstLeft pointers?
//FIXME Vojtech: The area is calculated with floats, it may not be numerically stable!
{
for (OutRec *outRec : m_PolyOuts)
if (outRec->Pts && !outRec->IsOpen && (outRec->IsHole ^ m_ReverseOutput) == (Area(*outRec) > 0))
ReversePolyPtLinks(outRec->Pts);
for (OutRec &outRec : m_PolyOuts)
if (outRec.Pts && !outRec.IsOpen && (outRec.IsHole ^ m_ReverseOutput) == (Area(outRec) > 0))
ReversePolyPtLinks(outRec.Pts);
}
JoinCommonEdges();
//unfortunately FixupOutPolygon() must be done after JoinCommonEdges()
{
for (OutRec *outRec : m_PolyOuts)
if (outRec->Pts) {
if (outRec->IsOpen)
for (OutRec &outRec : m_PolyOuts)
if (outRec.Pts) {
if (outRec.IsOpen)
// Removes duplicate points.
FixupOutPolyline(*outRec);
FixupOutPolyline(outRec);
else
// Removes duplicate points and simplifies consecutive parallel edges by removing the middle vertex.
FixupOutPolygon(*outRec);
FixupOutPolygon(outRec);
}
}
// For each polygon, search for exactly duplicate non-successive points.
@ -1194,22 +1174,18 @@ OutPt* Clipper::AllocateOutPt()
m_OutPtsFree = pt->Next;
} else if (m_OutPtsChunkLast < m_OutPtsChunkSize) {
// Get a point from the last chunk.
pt = m_OutPts.back() + (m_OutPtsChunkLast ++);
pt = &m_OutPts.back()[m_OutPtsChunkLast ++];
} else {
// The last chunk is full. Allocate a new one.
m_OutPts.push_back(new OutPt[m_OutPtsChunkSize]);
m_OutPts.emplace_back();
m_OutPtsChunkLast = 1;
pt = m_OutPts.back();
pt = &m_OutPts.back().front();
}
return pt;
}
void Clipper::DisposeAllOutRecs()
{
for (OutPt *pts : m_OutPts)
delete[] pts;
for (OutRec *rec : m_PolyOuts)
delete rec;
m_OutPts.clear();
m_OutPtsFree = nullptr;
m_OutPtsChunkLast = m_OutPtsChunkSize;
@ -1832,7 +1808,7 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt)
}
//------------------------------------------------------------------------------
void Clipper::SetHoleState(TEdge *e, OutRec *outrec) const
void Clipper::SetHoleState(TEdge *e, OutRec *outrec)
{
bool IsHole = false;
TEdge *e2 = e->PrevInAEL;
@ -1842,7 +1818,7 @@ void Clipper::SetHoleState(TEdge *e, OutRec *outrec) const
{
IsHole = !IsHole;
if (! outrec->FirstLeft)
outrec->FirstLeft = m_PolyOuts[e2->OutIdx];
outrec->FirstLeft = &m_PolyOuts[e2->OutIdx];
}
e2 = e2->PrevInAEL;
}
@ -1883,18 +1859,18 @@ bool Param1RightOfParam2(OutRec* outRec1, OutRec* outRec2)
OutRec* Clipper::GetOutRec(int Idx)
{
OutRec* outrec = m_PolyOuts[Idx];
while (outrec != m_PolyOuts[outrec->Idx])
outrec = m_PolyOuts[outrec->Idx];
OutRec* outrec = &m_PolyOuts[Idx];
while (outrec != &m_PolyOuts[outrec->Idx])
outrec = &m_PolyOuts[outrec->Idx];
return outrec;
}
//------------------------------------------------------------------------------
void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) const
void Clipper::AppendPolygon(TEdge *e1, TEdge *e2)
{
//get the start and ends of both output polygons ...
OutRec *outRec1 = m_PolyOuts[e1->OutIdx];
OutRec *outRec2 = m_PolyOuts[e2->OutIdx];
OutRec *outRec1 = &m_PolyOuts[e1->OutIdx];
OutRec *outRec2 = &m_PolyOuts[e2->OutIdx];
OutRec *holeStateRec;
if (Param1RightOfParam2(outRec1, outRec2))
@ -1991,16 +1967,16 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) const
OutRec* Clipper::CreateOutRec()
{
OutRec* result = new OutRec;
result->IsHole = false;
result->IsOpen = false;
result->FirstLeft = 0;
result->Pts = 0;
result->BottomPt = 0;
result->PolyNd = 0;
m_PolyOuts.push_back(result);
result->Idx = (int)m_PolyOuts.size()-1;
return result;
m_PolyOuts.emplace_back();
OutRec &result = m_PolyOuts.back();
result.IsHole = false;
result.IsOpen = false;
result.FirstLeft = 0;
result.Pts = 0;
result.BottomPt = 0;
result.PolyNd = 0;
result.Idx = (int)m_PolyOuts.size()-1;
return &result;
}
//------------------------------------------------------------------------------
@ -2022,7 +1998,7 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt)
return newOp;
} else
{
OutRec *outRec = m_PolyOuts[e->OutIdx];
OutRec *outRec = &m_PolyOuts[e->OutIdx];
//OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most'
OutPt* op = outRec->Pts;
@ -2045,7 +2021,7 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt)
OutPt* Clipper::GetLastOutPt(TEdge *e)
{
OutRec *outRec = m_PolyOuts[e->OutIdx];
OutRec *outRec = &m_PolyOuts[e->OutIdx];
if (e->Side == esLeft)
return outRec->Pts;
else
@ -2216,7 +2192,7 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge)
{
Direction dir;
cInt horzLeft, horzRight;
bool IsOpen = (horzEdge->OutIdx >= 0 && m_PolyOuts[horzEdge->OutIdx]->IsOpen);
bool IsOpen = (horzEdge->OutIdx >= 0 && m_PolyOuts[horzEdge->OutIdx].IsOpen);
GetHorzDirection(*horzEdge, dir, horzLeft, horzRight);
@ -2226,8 +2202,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) ...
@ -2600,7 +2576,7 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY)
if(IsMaximaEdge)
{
if (m_StrictSimple) m_Maxima.push_back(e->Top.x());
if (m_StrictSimple) m_Maxima.emplace_back(e->Top.x());
TEdge* ePrev = e->PrevInAEL;
DoMaxima(e);
if( !ePrev ) e = m_ActiveEdges;
@ -2778,12 +2754,12 @@ int PointCount(OutPt *Pts)
void Clipper::BuildResult(Paths &polys)
{
polys.reserve(m_PolyOuts.size());
for (OutRec* outRec : m_PolyOuts)
for (OutRec &outRec : m_PolyOuts)
{
assert(! outRec->IsOpen);
if (!outRec->Pts) continue;
assert(! outRec.IsOpen);
if (!outRec.Pts) continue;
Path pg;
OutPt* p = outRec->Pts->Prev;
OutPt* p = outRec.Pts->Prev;
int cnt = PointCount(p);
if (cnt < 2) continue;
pg.reserve(cnt);
@ -2802,31 +2778,31 @@ void Clipper::BuildResult2(PolyTree& polytree)
polytree.Clear();
polytree.AllNodes.reserve(m_PolyOuts.size());
//add each output polygon/contour to polytree ...
for (OutRec* outRec : m_PolyOuts)
for (OutRec &outRec : m_PolyOuts)
{
int cnt = PointCount(outRec->Pts);
if ((outRec->IsOpen && cnt < 2) || (!outRec->IsOpen && cnt < 3))
int cnt = PointCount(outRec.Pts);
if ((outRec.IsOpen && cnt < 2) || (!outRec.IsOpen && cnt < 3))
// Ignore an invalid output loop or a polyline.
continue;
//skip OutRecs that (a) contain outermost polygons or
//(b) already have the correct owner/child linkage ...
if (outRec->FirstLeft &&
(outRec->IsHole == outRec->FirstLeft->IsHole || ! outRec->FirstLeft->Pts)) {
OutRec* orfl = outRec->FirstLeft;
while (orfl && ((orfl->IsHole == outRec->IsHole) || !orfl->Pts))
if (outRec.FirstLeft &&
(outRec.IsHole == outRec.FirstLeft->IsHole || ! outRec.FirstLeft->Pts)) {
OutRec* orfl = outRec.FirstLeft;
while (orfl && ((orfl->IsHole == outRec.IsHole) || !orfl->Pts))
orfl = orfl->FirstLeft;
outRec->FirstLeft = orfl;
outRec.FirstLeft = orfl;
}
//nb: polytree takes ownership of all the PolyNodes
polytree.AllNodes.emplace_back(PolyNode());
PolyNode* pn = &polytree.AllNodes.back();
outRec->PolyNd = pn;
outRec.PolyNd = pn;
pn->Parent = 0;
pn->Index = 0;
pn->Contour.reserve(cnt);
OutPt *op = outRec->Pts->Prev;
OutPt *op = outRec.Pts->Prev;
for (int j = 0; j < cnt; j++)
{
pn->Contour.emplace_back(op->Pt);
@ -2836,18 +2812,18 @@ void Clipper::BuildResult2(PolyTree& polytree)
//fixup PolyNode links etc ...
polytree.Childs.reserve(m_PolyOuts.size());
for (OutRec* outRec : m_PolyOuts)
for (OutRec &outRec : m_PolyOuts)
{
if (!outRec->PolyNd) continue;
if (outRec->IsOpen)
if (!outRec.PolyNd) continue;
if (outRec.IsOpen)
{
outRec->PolyNd->m_IsOpen = true;
polytree.AddChild(*outRec->PolyNd);
outRec.PolyNd->m_IsOpen = true;
polytree.AddChild(*outRec.PolyNd);
}
else if (outRec->FirstLeft && outRec->FirstLeft->PolyNd)
outRec->FirstLeft->PolyNd->AddChild(*outRec->PolyNd);
else if (outRec.FirstLeft && outRec.FirstLeft->PolyNd)
outRec.FirstLeft->PolyNd->AddChild(*outRec.PolyNd);
else
polytree.AddChild(*outRec->PolyNd);
polytree.AddChild(*outRec.PolyNd);
}
}
//------------------------------------------------------------------------------
@ -3193,26 +3169,26 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2)
//----------------------------------------------------------------------
// This is potentially very expensive! O(n^3)!
void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const
void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec)
{
//tests if NewOutRec contains the polygon before reassigning FirstLeft
for (OutRec *outRec : m_PolyOuts)
for (OutRec &outRec : m_PolyOuts)
{
if (!outRec->Pts || !outRec->FirstLeft) continue;
OutRec* firstLeft = outRec->FirstLeft;
if (!outRec.Pts || !outRec.FirstLeft) continue;
OutRec* firstLeft = outRec.FirstLeft;
// Skip empty polygons.
while (firstLeft && !firstLeft->Pts) firstLeft = firstLeft->FirstLeft;
if (firstLeft == OldOutRec && Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts))
outRec->FirstLeft = NewOutRec;
if (firstLeft == OldOutRec && Poly2ContainsPoly1(outRec.Pts, NewOutRec->Pts))
outRec.FirstLeft = NewOutRec;
}
}
//----------------------------------------------------------------------
void Clipper::FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const
void Clipper::FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec)
{
//reassigns FirstLeft WITHOUT testing if NewOutRec contains the polygon
for (OutRec *outRec : m_PolyOuts)
if (outRec->FirstLeft == OldOutRec) outRec->FirstLeft = NewOutRec;
for (OutRec &outRec : m_PolyOuts)
if (outRec.FirstLeft == OldOutRec) outRec.FirstLeft = NewOutRec;
}
//----------------------------------------------------------------------
@ -3253,13 +3229,13 @@ void Clipper::JoinCommonEdges()
if (m_UsingPolyTree)
for (size_t j = 0; j < m_PolyOuts.size() - 1; j++)
{
OutRec* oRec = m_PolyOuts[j];
OutRec* firstLeft = oRec->FirstLeft;
OutRec &oRec = m_PolyOuts[j];
OutRec* firstLeft = oRec.FirstLeft;
while (firstLeft && !firstLeft->Pts) firstLeft = firstLeft->FirstLeft;
if (!oRec->Pts || firstLeft != outRec1 ||
oRec->IsHole == outRec1->IsHole) continue;
if (Poly2ContainsPoly1(oRec->Pts, join.OutPt2))
oRec->FirstLeft = outRec2;
if (!oRec.Pts || firstLeft != outRec1 ||
oRec.IsHole == outRec1->IsHole) continue;
if (Poly2ContainsPoly1(oRec.Pts, join.OutPt2))
oRec.FirstLeft = outRec2;
}
if (Poly2ContainsPoly1(outRec2->Pts, outRec1->Pts))
@ -3373,7 +3349,7 @@ void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType
break;
}
newNode->Contour.reserve(highI + 1);
newNode->Contour.push_back(path[0]);
newNode->Contour.emplace_back(path[0]);
int j = 0, k = 0;
for (int i = 1; i <= highI; i++) {
bool same = false;
@ -3386,7 +3362,7 @@ void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType
if (same)
continue;
j++;
newNode->Contour.push_back(path[i]);
newNode->Contour.emplace_back(path[i]);
if (path[i].y() > newNode->Contour[k].y() ||
(path[i].y() == newNode->Contour[k].y() &&
path[i].x() < newNode->Contour[k].x())) k = j;
@ -3514,7 +3490,7 @@ void ClipperOffset::DoOffset(double delta)
{
PolyNode& node = *m_polyNodes.Childs[i];
if (node.m_endtype == etClosedPolygon)
m_destPolys.push_back(node.Contour);
m_destPolys.emplace_back(node.Contour);
}
return;
}
@ -3556,7 +3532,7 @@ void ClipperOffset::DoOffset(double delta)
double X = 1.0, Y = 0.0;
for (cInt j = 1; j <= steps; j++)
{
m_destPoly.push_back(IntPoint2d(
m_destPoly.emplace_back(IntPoint2d(
Round(m_srcPoly[0].x() + X * delta),
Round(m_srcPoly[0].y() + Y * delta)));
double X2 = X;
@ -3569,7 +3545,7 @@ void ClipperOffset::DoOffset(double delta)
double X = -1.0, Y = -1.0;
for (int j = 0; j < 4; ++j)
{
m_destPoly.push_back(IntPoint2d(
m_destPoly.emplace_back(IntPoint2d(
Round(m_srcPoly[0].x() + X * delta),
Round(m_srcPoly[0].y() + Y * delta)));
if (X < 0) X = 1;
@ -3577,32 +3553,32 @@ void ClipperOffset::DoOffset(double delta)
else X = -1;
}
}
m_destPolys.push_back(m_destPoly);
m_destPolys.emplace_back(m_destPoly);
continue;
}
//build m_normals ...
m_normals.clear();
m_normals.reserve(len);
for (int j = 0; j < len - 1; ++j)
m_normals.push_back(GetUnitNormal(m_srcPoly[j], m_srcPoly[j + 1]));
m_normals.emplace_back(GetUnitNormal(m_srcPoly[j], m_srcPoly[j + 1]));
if (node.m_endtype == etClosedLine || node.m_endtype == etClosedPolygon)
m_normals.push_back(GetUnitNormal(m_srcPoly[len - 1], m_srcPoly[0]));
m_normals.emplace_back(GetUnitNormal(m_srcPoly[len - 1], m_srcPoly[0]));
else
m_normals.push_back(DoublePoint(m_normals[len - 2]));
m_normals.emplace_back(DoublePoint(m_normals[len - 2]));
if (node.m_endtype == etClosedPolygon)
{
int k = len - 1;
for (int j = 0; j < len; ++j)
OffsetPoint(j, k, node.m_jointype);
m_destPolys.push_back(m_destPoly);
m_destPolys.emplace_back(m_destPoly);
}
else if (node.m_endtype == etClosedLine)
{
int k = len - 1;
for (int j = 0; j < len; ++j)
OffsetPoint(j, k, node.m_jointype);
m_destPolys.push_back(m_destPoly);
m_destPolys.emplace_back(m_destPoly);
m_destPoly.clear();
//re-build m_normals ...
DoublePoint n = m_normals[len -1];
@ -3612,7 +3588,7 @@ void ClipperOffset::DoOffset(double delta)
k = 0;
for (int j = len - 1; j >= 0; j--)
OffsetPoint(j, k, node.m_jointype);
m_destPolys.push_back(m_destPoly);
m_destPolys.emplace_back(m_destPoly);
}
else
{
@ -3625,9 +3601,9 @@ void ClipperOffset::DoOffset(double delta)
{
int j = len - 1;
pt1 = IntPoint2d(Round(m_srcPoly[j].x() + m_normals[j].x() * delta), Round(m_srcPoly[j].y() + m_normals[j].y() * delta));
m_destPoly.push_back(pt1);
m_destPoly.emplace_back(pt1);
pt1 = IntPoint2d(Round(m_srcPoly[j].x() - m_normals[j].x() * delta), Round(m_srcPoly[j].y() - m_normals[j].y() * delta));
m_destPoly.push_back(pt1);
m_destPoly.emplace_back(pt1);
}
else
{
@ -3652,9 +3628,9 @@ void ClipperOffset::DoOffset(double delta)
if (node.m_endtype == etOpenButt)
{
pt1 = IntPoint2d(Round(m_srcPoly[0].x() - m_normals[0].x() * delta), Round(m_srcPoly[0].y() - m_normals[0].y() * delta));
m_destPoly.push_back(pt1);
m_destPoly.emplace_back(pt1);
pt1 = IntPoint2d(Round(m_srcPoly[0].x() + m_normals[0].x() * delta), Round(m_srcPoly[0].y() + m_normals[0].y() * delta));
m_destPoly.push_back(pt1);
m_destPoly.emplace_back(pt1);
}
else
{
@ -3665,7 +3641,7 @@ void ClipperOffset::DoOffset(double delta)
else
DoRound(0, 1);
}
m_destPolys.push_back(m_destPoly);
m_destPolys.emplace_back(m_destPoly);
}
}
}
@ -3681,7 +3657,7 @@ void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype)
double cosA = (m_normals[k].x() * m_normals[j].x() + m_normals[j].y() * m_normals[k].y() );
if (cosA > 0) // angle => 0 degrees
{
m_destPoly.push_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[k].x() * m_delta),
m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[k].x() * m_delta),
Round(m_srcPoly[j].y() + m_normals[k].y() * m_delta)));
return;
}
@ -3692,10 +3668,10 @@ void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype)
if (m_sinA * m_delta < 0)
{
m_destPoly.push_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[k].x() * m_delta),
m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[k].x() * m_delta),
Round(m_srcPoly[j].y() + m_normals[k].y() * m_delta)));
m_destPoly.push_back(m_srcPoly[j]);
m_destPoly.push_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[j].x() * m_delta),
m_destPoly.emplace_back(m_srcPoly[j]);
m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[j].x() * m_delta),
Round(m_srcPoly[j].y() + m_normals[j].y() * m_delta)));
}
else
@ -3719,10 +3695,10 @@ void ClipperOffset::DoSquare(int j, int k)
{
double dx = std::tan(std::atan2(m_sinA,
m_normals[k].x() * m_normals[j].x() + m_normals[k].y() * m_normals[j].y()) / 4);
m_destPoly.push_back(IntPoint2d(
m_destPoly.emplace_back(IntPoint2d(
Round(m_srcPoly[j].x() + m_delta * (m_normals[k].x() - m_normals[k].y() * dx)),
Round(m_srcPoly[j].y() + m_delta * (m_normals[k].y() + m_normals[k].x() * dx))));
m_destPoly.push_back(IntPoint2d(
m_destPoly.emplace_back(IntPoint2d(
Round(m_srcPoly[j].x() + m_delta * (m_normals[j].x() + m_normals[j].y() * dx)),
Round(m_srcPoly[j].y() + m_delta * (m_normals[j].y() - m_normals[j].x() * dx))));
}
@ -3731,7 +3707,7 @@ void ClipperOffset::DoSquare(int j, int k)
void ClipperOffset::DoMiter(int j, int k, double r)
{
double q = m_delta / r;
m_destPoly.push_back(IntPoint2d(Round(m_srcPoly[j].x() + (m_normals[k].x() + m_normals[j].x()) * q),
m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + (m_normals[k].x() + m_normals[j].x()) * q),
Round(m_srcPoly[j].y() + (m_normals[k].y() + m_normals[j].y()) * q)));
}
//------------------------------------------------------------------------------
@ -3745,14 +3721,14 @@ void ClipperOffset::DoRound(int j, int k)
double X = m_normals[k].x(), Y = m_normals[k].y(), X2;
for (int i = 0; i < steps; ++i)
{
m_destPoly.push_back(IntPoint2d(
m_destPoly.emplace_back(IntPoint2d(
Round(m_srcPoly[j].x() + X * m_delta),
Round(m_srcPoly[j].y() + Y * m_delta)));
X2 = X;
X = X * m_cos - m_sin * Y;
Y = X2 * m_sin + Y * m_cos;
}
m_destPoly.push_back(IntPoint2d(
m_destPoly.emplace_back(IntPoint2d(
Round(m_srcPoly[j].x() + m_normals[j].x() * m_delta),
Round(m_srcPoly[j].y() + m_normals[j].y() * m_delta)));
}
@ -3771,13 +3747,13 @@ void Clipper::DoSimplePolygons()
size_t i = 0;
while (i < m_PolyOuts.size())
{
OutRec* outrec = m_PolyOuts[i++];
OutPt* op = outrec->Pts;
if (!op || outrec->IsOpen) continue;
OutRec &outrec = m_PolyOuts[i++];
OutPt* op = outrec.Pts;
if (!op || outrec.IsOpen) continue;
do //for each Pt in Polygon until duplicate found do ...
{
OutPt* op2 = op->Next;
while (op2 != outrec->Pts)
while (op2 != outrec.Pts)
{
if ((op->Pt == op2->Pt) && op2->Next != op && op2->Prev != op)
{
@ -3789,37 +3765,37 @@ void Clipper::DoSimplePolygons()
op2->Prev = op3;
op3->Next = op2;
outrec->Pts = op;
outrec.Pts = op;
OutRec* outrec2 = CreateOutRec();
outrec2->Pts = op2;
UpdateOutPtIdxs(*outrec2);
if (Poly2ContainsPoly1(outrec2->Pts, outrec->Pts))
if (Poly2ContainsPoly1(outrec2->Pts, outrec.Pts))
{
//OutRec2 is contained by OutRec1 ...
outrec2->IsHole = !outrec->IsHole;
outrec2->FirstLeft = outrec;
outrec2->IsHole = !outrec.IsHole;
outrec2->FirstLeft = &outrec;
// For each m_PolyOuts, replace FirstLeft from outRec2 to outrec.
if (m_UsingPolyTree) FixupFirstLefts2(outrec2, outrec);
if (m_UsingPolyTree) FixupFirstLefts2(outrec2, &outrec);
}
else
if (Poly2ContainsPoly1(outrec->Pts, outrec2->Pts))
if (Poly2ContainsPoly1(outrec.Pts, outrec2->Pts))
{
//OutRec1 is contained by OutRec2 ...
outrec2->IsHole = outrec->IsHole;
outrec->IsHole = !outrec2->IsHole;
outrec2->FirstLeft = outrec->FirstLeft;
outrec->FirstLeft = outrec2;
outrec2->IsHole = outrec.IsHole;
outrec.IsHole = !outrec2->IsHole;
outrec2->FirstLeft = outrec.FirstLeft;
outrec.FirstLeft = outrec2;
// For each m_PolyOuts, replace FirstLeft from outrec to outrec2.
if (m_UsingPolyTree) FixupFirstLefts2(outrec, outrec2);
if (m_UsingPolyTree) FixupFirstLefts2(&outrec, outrec2);
}
else
{
//the 2 polygons are separate ...
outrec2->IsHole = outrec->IsHole;
outrec2->FirstLeft = outrec->FirstLeft;
outrec2->IsHole = outrec.IsHole;
outrec2->FirstLeft = outrec.FirstLeft;
// For each polygon of m_PolyOuts, replace FirstLeft from outrec to outrec2 if the polygon is inside outRec2.
//FIXME This is potentially very expensive! O(n^3)!
if (m_UsingPolyTree) FixupFirstLefts1(outrec, outrec2);
if (m_UsingPolyTree) FixupFirstLefts1(&outrec, outrec2);
}
op2 = op; //ie get ready for the Next iteration
}
@ -3827,7 +3803,7 @@ void Clipper::DoSimplePolygons()
}
op = op->Next;
}
while (op != outrec->Pts);
while (op != outrec.Pts);
}
}
//------------------------------------------------------------------------------
@ -3845,10 +3821,10 @@ void ReversePaths(Paths& p)
}
//------------------------------------------------------------------------------
Paths SimplifyPolygon(const Path &in_poly, PolyFillType fillType)
Paths SimplifyPolygon(const Path &in_poly, PolyFillType fillType, bool strictly_simple /* = true */)
{
Clipper c;
c.StrictlySimple(true);
c.StrictlySimple(strictly_simple);
c.AddPath(in_poly, ptSubject, true);
Paths out;
c.Execute(ctUnion, out, fillType, fillType);
@ -3941,7 +3917,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];
@ -4020,8 +3996,8 @@ void Minkowski(const Path& poly, const Path& path,
Path p;
p.reserve(polyCnt);
for (size_t j = 0; j < poly.size(); ++j)
p.push_back(IntPoint2d(path[i].x() + poly[j].x(), path[i].y() + poly[j].y()));
pp.push_back(p);
p.emplace_back(IntPoint2d(path[i].x() + poly[j].x(), path[i].y() + poly[j].y()));
pp.emplace_back(p);
}
else
for (size_t i = 0; i < pathCnt; ++i)
@ -4029,8 +4005,8 @@ void Minkowski(const Path& poly, const Path& path,
Path p;
p.reserve(polyCnt);
for (size_t j = 0; j < poly.size(); ++j)
p.push_back(IntPoint2d(path[i].x() - poly[j].x(), path[i].y() - poly[j].y()));
pp.push_back(p);
p.emplace_back(IntPoint2d(path[i].x() - poly[j].x(), path[i].y() - poly[j].y()));
pp.emplace_back(p);
}
solution.clear();
@ -4040,12 +4016,12 @@ void Minkowski(const Path& poly, const Path& path,
{
Path quad;
quad.reserve(4);
quad.push_back(pp[i % pathCnt][j % polyCnt]);
quad.push_back(pp[(i + 1) % pathCnt][j % polyCnt]);
quad.push_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]);
quad.push_back(pp[i % pathCnt][(j + 1) % polyCnt]);
quad.emplace_back(pp[i % pathCnt][j % polyCnt]);
quad.emplace_back(pp[(i + 1) % pathCnt][j % polyCnt]);
quad.emplace_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]);
quad.emplace_back(pp[i % pathCnt][(j + 1) % polyCnt]);
if (!Orientation(quad)) ReversePath(quad);
solution.push_back(quad);
solution.emplace_back(quad);
}
}
//------------------------------------------------------------------------------
@ -4105,7 +4081,7 @@ void AddPolyNodeToPaths(const PolyNode& polynode, NodeType nodetype, Paths& path
else if (nodetype == ntOpen) return;
if (!polynode.Contour.empty() && match)
paths.push_back(polynode.Contour);
paths.emplace_back(polynode.Contour);
for (int i = 0; i < polynode.ChildCount(); ++i)
AddPolyNodeToPaths(*polynode.Childs[i], nodetype, paths);
}
@ -4117,7 +4093,7 @@ void AddPolyNodeToPaths(PolyNode&& polynode, NodeType nodetype, Paths& paths)
else if (nodetype == ntOpen) return;
if (!polynode.Contour.empty() && match)
paths.push_back(std::move(polynode.Contour));
paths.emplace_back(std::move(polynode.Contour));
for (int i = 0; i < polynode.ChildCount(); ++i)
AddPolyNodeToPaths(std::move(*polynode.Childs[i]), nodetype, paths);
}
@ -4155,7 +4131,7 @@ void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths)
//Open paths are top level only, so ...
for (int i = 0; i < polytree.ChildCount(); ++i)
if (polytree.Childs[i]->IsOpen())
paths.push_back(polytree.Childs[i]->Contour);
paths.emplace_back(polytree.Childs[i]->Contour);
}
//------------------------------------------------------------------------------

View File

@ -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.
@ -50,6 +52,7 @@
//use_deprecated: Enables temporary support for the obsolete functions
//#define use_deprecated
#include <array>
#include <vector>
#include <deque>
#include <stdexcept>
@ -112,8 +115,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 +139,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 +192,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
};
@ -194,7 +200,8 @@ double Area(const Path &poly);
inline bool Orientation(const Path &poly) { return Area(poly) >= 0; }
int PointInPolygon(const IntPoint &pt, const Path &path);
Paths SimplifyPolygon(const Path &in_poly, PolyFillType fillType = pftEvenOdd);
// Union with "strictly simple" fix enabled.
Paths SimplifyPolygon(const Path &in_poly, PolyFillType fillType = pftNonZero, bool strictly_simple = true);
void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415);
void CleanPolygon(Path& poly, double distance = 1.415);
@ -277,7 +284,27 @@ enum EdgeSide { esLeft = 1, esRight = 2};
OutPt *Prev;
};
struct OutRec;
using OutPts = std::vector<OutPt, Allocator<OutPt>>;
// Output polygon.
struct OutRec {
int Idx;
bool IsHole;
bool IsOpen;
//The 'FirstLeft' field points to another OutRec that contains or is the
//'parent' of OutRec. It is 'first left' because the ActiveEdgeList (AEL) is
//parsed left from the current edge (owning OutRec) until the owner OutRec
//is found. This field simplifies sorting the polygons into a tree structure
//which reflects the parent/child relationships of all polygons.
//This field should be renamed Parent, and will be later.
OutRec* FirstLeft;
// Used only by void Clipper::BuildResult2(PolyTree& polytree)
PolyNode* PolyNd;
// Linked list of output points, dynamically allocated.
OutPt* Pts;
OutPt* BottomPt;
};
struct Join {
Join(OutPt *OutPt1, OutPt *OutPt2, IntPoint OffPt) :
OutPt1(OutPt1), OutPt2(OutPt2), OffPt(OffPt) {}
@ -312,7 +339,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 +360,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 +396,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 +407,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 +452,23 @@ protected:
private:
// Output polygons.
std::vector<OutRec*> m_PolyOuts;
std::deque<OutRec, Allocator<OutRec>> m_PolyOuts;
// Output points, allocated by a continuous sets of m_OutPtsChunkSize.
std::vector<OutPt*> m_OutPts;
static constexpr const size_t m_OutPtsChunkSize = 32;
std::deque<std::array<OutPt, m_OutPtsChunkSize>, Allocator<std::array<OutPt, m_OutPtsChunkSize>>> 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;
@ -473,7 +502,7 @@ private:
void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
OutRec* GetOutRec(int idx);
void AppendPolygon(TEdge *e1, TEdge *e2) const;
void AppendPolygon(TEdge *e1, TEdge *e2);
void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt);
OutRec* CreateOutRec();
OutPt* AddOutPt(TEdge *e, const IntPoint &pt);
@ -489,7 +518,7 @@ private:
void ProcessEdgesAtTopOfScanbeam(const cInt topY);
void BuildResult(Paths& polys);
void BuildResult2(PolyTree& polytree);
void SetHoleState(TEdge *e, OutRec *outrec) const;
void SetHoleState(TEdge *e, OutRec *outrec);
bool FixupIntersectionOrder();
void FixupOutPolygon(OutRec &outrec);
void FixupOutPolyline(OutRec &outrec);
@ -499,8 +528,8 @@ private:
bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, const IntPoint &Pt, bool DiscardLeft);
void JoinCommonEdges();
void DoSimplePolygons();
void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const;
void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const;
void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec);
void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec);
#ifdef CLIPPERLIB_USE_XYZ
void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2);
#endif
@ -530,7 +559,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
@ -558,10 +587,11 @@ class clipperException : public std::exception
};
//------------------------------------------------------------------------------
// Union with "strictly simple" fix enabled.
template<typename PathsProvider>
inline Paths SimplifyPolygons(PathsProvider &&in_polys, PolyFillType fillType = pftEvenOdd) {
inline Paths SimplifyPolygons(PathsProvider &&in_polys, PolyFillType fillType = pftNonZero, bool strictly_simple = true) {
Clipper c;
c.StrictlySimple(true);
c.StrictlySimple(strictly_simple);
c.AddPaths(std::forward<PathsProvider>(in_polys), ptSubject, true);
Paths out;
c.Execute(ctUnion, out, fillType, fillType);

View File

@ -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)

View File

@ -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

View File

@ -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())
{

View File

@ -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

View File

@ -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)

View File

@ -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"
@ -233,7 +232,7 @@ std::unique_ptr<LocToLineGrid> cre
void fixSelfIntersections(const coord_t epsilon, Polygons &thiss)
{
if (epsilon < 1) {
ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss));
ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss), ClipperLib::pftEvenOdd);
return;
}
@ -274,7 +273,7 @@ void fixSelfIntersections(const coord_t epsilon, Polygons &thiss)
}
}
ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss));
ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss), ClipperLib::pftEvenOdd);
}
/*!
@ -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,

View File

@ -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:
/*!

View File

@ -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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -138,9 +138,9 @@ public:
return Point(coord_t(p.x() * matrix[0] + p.y() * matrix[2]), coord_t(p.x() * matrix[1] + p.y() * matrix[3]));
}
};
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();

View File

@ -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)
{

View File

@ -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 &center) 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();

View File

@ -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();
}

View File

@ -131,6 +131,8 @@ set(SLIC3R_SOURCES
Format/AnycubicSLA.cpp
Format/STEP.hpp
Format/STEP.cpp
Format/SLAArchiveFormatRegistry.hpp
Format/SLAArchiveFormatRegistry.cpp
GCode/ThumbnailData.cpp
GCode/ThumbnailData.hpp
GCode/Thumbnails.cpp
@ -276,10 +278,21 @@ set(SLIC3R_SOURCES
SlicingAdaptive.hpp
Subdivide.cpp
Subdivide.hpp
Support/SupportCommon.cpp
Support/SupportCommon.hpp
Support/SupportDebug.cpp
Support/SupportDebug.hpp
Support/SupportLayer.hpp
Support/SupportMaterial.cpp
Support/SupportMaterial.hpp
Support/SupportParameters.cpp
Support/SupportParameters.hpp
Support/TreeSupport.cpp
Support/TreeSupport.hpp
Support/TreeModelVolumes.cpp
Support/TreeModelVolumes.hpp
SupportSpotsGenerator.cpp
SupportSpotsGenerator.hpp
SupportMaterial.cpp
SupportMaterial.hpp
Surface.cpp
Surface.hpp
SurfaceCollection.cpp
@ -291,10 +304,6 @@ set(SLIC3R_SOURCES
Tesselate.cpp
Tesselate.hpp
TextConfiguration.hpp
TreeSupport.cpp
TreeSupport.hpp
TreeModelVolumes.cpp
TreeModelVolumes.hpp
TriangleMesh.cpp
TriangleMesh.hpp
TriangleMeshSlicer.cpp
@ -485,6 +494,7 @@ target_link_libraries(libslic3r
qhull
semver
TBB::tbb
TBB::tbbmalloc
libslic3r_cgal
${CMAKE_DL_LIBS}
PNG::PNG

View File

@ -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.
@ -656,6 +685,8 @@ Slic3r::Polygons diff(const Slic3r::Surfaces &subject, const Slic3r::Polygons &c
{ return _clipper(ClipperLib::ctDifference, ClipperUtils::SurfacesProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); }
Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset)
{ return _clipper(ClipperLib::ctIntersection, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::SinglePathProvider(clip.points), do_safety_offset); }
Slic3r::Polygons intersection_clipped(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
{ return intersection(subject, ClipperUtils::clip_clipper_polygons_with_subject_bbox(clip, get_extents(subject).inflated(SCALED_EPSILON)), do_safety_offset); }
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset)
{ return _clipper(ClipperLib::ctIntersection, ClipperUtils::PolygonsProvider(subject), ClipperUtils::ExPolygonProvider(clip), do_safety_offset); }
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
@ -750,6 +781,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);
@ -933,31 +966,30 @@ Polygons union_pt_chained_outside_in(const Polygons &subject)
return retval;
}
Polygons simplify_polygons(const Polygons &subject, bool preserve_collinear)
Polygons simplify_polygons(const Polygons &subject)
{
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
ClipperLib::Paths output;
if (preserve_collinear) {
ClipperLib::Clipper c;
c.PreserveCollinear(true);
c.StrictlySimple(true);
c.AddPaths(ClipperUtils::PolygonsProvider(subject), ClipperLib::ptSubject, true);
c.Execute(ClipperLib::ctUnion, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
} else {
output = ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(subject), ClipperLib::pftNonZero);
}
ClipperLib::Clipper c;
// c.PreserveCollinear(true);
//FIXME StrictlySimple is very expensive! Is it needed?
c.StrictlySimple(true);
c.AddPaths(ClipperUtils::PolygonsProvider(subject), ClipperLib::ptSubject, true);
c.Execute(ClipperLib::ctUnion, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
// convert into Slic3r polygons
return to_polygons(std::move(output));
}
ExPolygons simplify_polygons_ex(const Polygons &subject, bool preserve_collinear)
{
if (! preserve_collinear)
return union_ex(simplify_polygons(subject, false));
CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT);
ClipperLib::PolyTree polytree;
ClipperLib::Clipper c;
c.PreserveCollinear(true);
// c.PreserveCollinear(true);
//FIXME StrictlySimple is very expensive! Is it needed?
c.StrictlySimple(true);
c.AddPaths(ClipperUtils::PolygonsProvider(subject), ClipperLib::ptSubject, true);
c.Execute(ClipperLib::ctUnion, polytree, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
@ -968,6 +1000,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 +1025,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 +1045,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 +1067,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 +1207,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 +1257,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 +1266,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 +1395,7 @@ ExPolygons variable_offset_inner_ex(const ExPolygon &expoly, const std::vector<s
output = PolyTreeToExPolygons(std::move(polytree));
}
assert(output.size() == 1);
return output;
}

View File

@ -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) {}
@ -453,6 +453,9 @@ inline Slic3r::Lines diff_ln(const Slic3r::Lines &subject, const Slic3r::Polygon
Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
// Optimized version clipping the "clipping" polygon using clip_clipper_polygon_with_subject_bbox().
// To be used with complex clipping polygons, where majority of the clipping polygons are outside of the source polygon.
Slic3r::Polygons intersection_clipped(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polygons intersection(const Slic3r::ExPolygon &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polygons intersection(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polygons intersection(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
@ -596,8 +599,8 @@ void traverse_pt(const ClipperLib::PolyNodes &nodes, ExOrJustPolygons *retval)
/* OTHER */
Slic3r::Polygons simplify_polygons(const Slic3r::Polygons &subject, bool preserve_collinear = false);
Slic3r::ExPolygons simplify_polygons_ex(const Slic3r::Polygons &subject, bool preserve_collinear = false);
Slic3r::Polygons simplify_polygons(const Slic3r::Polygons &subject);
Slic3r::ExPolygons simplify_polygons_ex(const Slic3r::Polygons &subject);
Polygons top_level_islands(const Slic3r::Polygons &polygons);

View File

@ -40,7 +40,7 @@ inline ZPath to_zpath(const Points &path, coord_t z)
// Convert multiple paths to paths with a given Z coordinate.
// 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;
}

View File

@ -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);
}

View File

@ -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; }

View File

@ -597,7 +597,8 @@ ExPolygon elephant_foot_compensation(const ExPolygon &input_expoly, double min_c
}
ExPolygons out_vec = variable_offset_inner_ex(resampled, deltas, 2.);
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);
}
}

View File

@ -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;
}

View File

@ -10,6 +10,8 @@
#include <cassert>
#include <list>
#include <ankerl/unordered_dense.h>
namespace Slic3r {
void ExPolygon::scale(double factor)
@ -182,14 +184,14 @@ Polygons ExPolygon::simplify_p(double tolerance) const
{
Polygon p = this->contour;
p.points.push_back(p.points.front());
p.points = MultiPoint::_douglas_peucker(p.points, tolerance);
p.points = MultiPoint::douglas_peucker(p.points, tolerance);
p.points.pop_back();
pp.emplace_back(std::move(p));
}
// holes
for (Polygon p : this->holes) {
p.points.push_back(p.points.front());
p.points = MultiPoint::_douglas_peucker(p.points, tolerance);
p.points = MultiPoint::douglas_peucker(p.points, tolerance);
p.points.pop_back();
pp.emplace_back(std::move(p));
}
@ -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)

View File

@ -176,7 +176,7 @@ Flow Flow::with_cross_section(float area_new) const
return this->with_width(width_new);
} else {
// Create a rounded extrusion.
auto dmr = float(sqrt(area_new / M_PI));
auto dmr = 2.0 * float(sqrt(area_new / M_PI));
return Flow(dmr, dmr, m_spacing, m_nozzle_diameter, false);
}
} else

View File

@ -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);

View File

@ -4,44 +4,14 @@
#include <string>
#include "SLAArchiveWriter.hpp"
#include "SLAArchiveFormatRegistry.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),
*/
constexpr uint16_t ANYCUBIC_SLA_FORMAT_VERSION_1 = 1;
constexpr uint16_t ANYCUBIC_SLA_FORMAT_VERSION_515 = 515;
constexpr uint16_t ANYCUBIC_SLA_FORMAT_VERSION_516 = 516;
constexpr uint16_t ANYCUBIC_SLA_FORMAT_VERSION_517 = 517;
namespace Slic3r {
@ -75,6 +45,21 @@ public:
const std::string &projectname = "") override;
};
inline Slic3r::ArchiveEntry anycubic_sla_format_versioned(const char *fileformat, const char *desc, uint16_t version)
{
Slic3r::ArchiveEntry entry(fileformat);
entry.desc = desc;
entry.ext = fileformat;
entry.wrfactoryfn = [version] (const auto &cfg) { return std::make_unique<AnycubicSLAArchive>(cfg, version); };
return entry;
}
inline Slic3r::ArchiveEntry anycubic_sla_format(const char *fileformat, const char *desc)
{
return anycubic_sla_format_versioned(fileformat, desc, ANYCUBIC_SLA_FORMAT_VERSION_1);
}
} // namespace Slic3r::sla

View File

@ -17,6 +17,7 @@
#include "libslic3r/GCode/ThumbnailData.hpp"
#include "SLAArchiveReader.hpp"
#include "SLAArchiveFormatRegistry.hpp"
#include "ZipperArchiveImport.hpp"
#include "libslic3r/MarchingSquares.hpp"
@ -26,6 +27,7 @@
#include "libslic3r/SLA/RasterBase.hpp"
#include <boost/property_tree/ini_parser.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/algorithm/string.hpp>

View File

@ -0,0 +1,147 @@
#include <set>
#include <mutex>
#include <memory>
#include "SL1.hpp"
#include "SL1_SVG.hpp"
#include "AnycubicSLA.hpp"
#include "SLAArchiveFormatRegistry.hpp"
namespace Slic3r {
static std::mutex arch_mtx;
class Registry {
static std::unique_ptr<Registry> registry;
std::set<ArchiveEntry> entries;
public:
Registry ()
{
entries = {
{
"SL1", // id
L("SL1 archive format"), // description
"sl1", // main extension
{"sl1s", "zip"}, // extension aliases
// Writer factory
[] (const auto &cfg) { return std::make_unique<SL1Archive>(cfg); },
// Reader factory
[] (const std::string &fname, SLAImportQuality quality, const ProgrFn &progr) {
return std::make_unique<SL1Reader>(fname, quality, progr);
}
},
{
"SL1SVG",
L("SL1SVG archive files"),
"sl1_svg",
{},
[] (const auto &cfg) { return std::make_unique<SL1_SVGArchive>(cfg); },
[] (const std::string &fname, SLAImportQuality quality, const ProgrFn &progr) {
return std::make_unique<SL1_SVGReader>(fname, quality, progr);
}
},
{
"SL2",
"",
"sl1_svg",
{},
[] (const auto &cfg) { return std::make_unique<SL1_SVGArchive>(cfg); },
nullptr
},
anycubic_sla_format("pwmo", "Photon Mono"),
anycubic_sla_format("pwmx", "Photon Mono X"),
anycubic_sla_format("pwms", "Photon Mono SE"),
/**
// 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),
*/
};
}
static Registry& get_instance()
{
if (!registry)
registry = std::make_unique<Registry>();
return *registry;
}
static std::set<ArchiveEntry>& get()
{
return get_instance().entries;
}
std::set<ArchiveEntry>& get_entries() { return entries; }
};
std::unique_ptr<Registry> Registry::registry = nullptr;
std::set<ArchiveEntry> registered_sla_archives()
{
std::lock_guard lk{arch_mtx};
return Registry::get();
}
std::vector<std::string> get_extensions(const ArchiveEntry &entry)
{
auto ret = reserve_vector<std::string>(entry.ext_aliases.size() + 1);
ret.emplace_back(entry.ext);
for (const char *alias : entry.ext_aliases)
ret.emplace_back(alias);
return ret;
}
ArchiveWriterFactory get_writer_factory(const char *formatid)
{
std::lock_guard lk{arch_mtx};
ArchiveWriterFactory ret;
auto entry = Registry::get().find(ArchiveEntry{formatid});
if (entry != Registry::get().end())
ret = entry->wrfactoryfn;
return ret;
}
ArchiveReaderFactory get_reader_factory(const char *formatid)
{
std::lock_guard lk{arch_mtx};
ArchiveReaderFactory ret;
auto entry = Registry::get().find(ArchiveEntry{formatid});
if (entry != Registry::get().end())
ret = entry->rdfactoryfn;
return ret;
}
} // namespace Slic3r::sla

View File

@ -0,0 +1,71 @@
#ifndef SLA_ARCHIVE_FORMAT_REGISTRY_HPP
#define SLA_ARCHIVE_FORMAT_REGISTRY_HPP
#include "SLAArchiveWriter.hpp"
#include "SLAArchiveReader.hpp"
#include <cstring>
namespace Slic3r {
// Factory function that returns an implementation of SLAArchiveWriter given
// a printer configuration.
using ArchiveWriterFactory = std::function<
std::unique_ptr<SLAArchiveWriter>(const SLAPrinterConfig &)
>;
// Factory function that returns an implementation of SLAArchiveReader
using ArchiveReaderFactory = std::function<
std::unique_ptr<SLAArchiveReader>(const std::string &fname,
SLAImportQuality quality,
const ProgrFn & progr)
>;
struct ArchiveEntry {
// Main ID for the format, for internal unique identification
const char *id;
// Generic description (usable in GUI) about an archive format. Should only
// be marked for localization (macro L).
const char *desc = "";
// Main extension of the format.
const char *ext = "zip";
ArchiveWriterFactory wrfactoryfn;
ArchiveReaderFactory rdfactoryfn;
// Secondary, alias extensions
std::vector<const char *> ext_aliases;
explicit ArchiveEntry(const char *formatid) : id{formatid} {}
ArchiveEntry(const char *formatid,
const char *description,
const char *extension,
std::initializer_list<const char *> extaliases,
const ArchiveWriterFactory &wrfn,
const ArchiveReaderFactory &rdfn)
: id{formatid}
, desc{description}
, ext{extension}
, ext_aliases{extaliases}
, wrfactoryfn{wrfn}
, rdfactoryfn{rdfn}
{}
bool operator <(const ArchiveEntry &other) const
{
return std::strcmp(id, other.id) < 0;
}
};
std::vector<std::string> get_extensions(const ArchiveEntry &entry);
std::set<ArchiveEntry> registered_sla_archives();
ArchiveWriterFactory get_writer_factory(const char *formatid);
ArchiveReaderFactory get_reader_factory(const char *formatid);
} // namespace Slic3r
#endif // ARCHIVEREGISTRY_HPP

View File

@ -8,44 +8,13 @@
#include <boost/filesystem/path.hpp>
#include <boost/algorithm/string.hpp>
#include "SLAArchiveFormatRegistry.hpp"
#include <array>
#include <map>
namespace Slic3r {
namespace {
// Factory function that returns an implementation of SLAArchiveReader.
using ArchiveFactory = std::function<
std::unique_ptr<SLAArchiveReader>(const std::string &fname,
SLAImportQuality quality,
const ProgrFn & progr)>;
// Entry in the global registry of readable archive formats.
struct ArchiveEntry {
const char *descr;
std::vector<const char *> extensions;
ArchiveFactory factoryfn;
};
// This is where the readable archive formats are registered.
static const std::map<std::string, ArchiveEntry> REGISTERED_ARCHIVES {
{
"SL1",
{ L("SL1 / SL1S archive files"), {"sl1", "sl1s", "zip"},
[] (const std::string &fname, SLAImportQuality quality, const ProgrFn &progr) { return std::make_unique<SL1Reader>(fname, quality, progr); } }
},
{
"SL1SVG",
{ L("SL1SVG archive files"), {"sl1_svg"/*, "zip"*/}, // also a zip but unnecessary hassle to implement single extension for multiple archives
[] (const std::string &fname, SLAImportQuality quality, const ProgrFn &progr) { return std::make_unique<SL1_SVGReader>(fname, quality, progr); }}
},
// TODO: pwmx and future others.
};
} // namespace
std::unique_ptr<SLAArchiveReader> SLAArchiveReader::create(
const std::string &fname,
const std::string &format_id,
@ -64,11 +33,13 @@ std::unique_ptr<SLAArchiveReader> SLAArchiveReader::create(
std::unique_ptr<SLAArchiveReader> ret;
auto arch_from = REGISTERED_ARCHIVES.begin();
auto arch_to = REGISTERED_ARCHIVES.end();
auto registry = registered_sla_archives();
auto arch_it = REGISTERED_ARCHIVES.find(format_id);
if (arch_it != REGISTERED_ARCHIVES.end()) {
auto arch_from = registry.begin();
auto arch_to = registry.end();
auto arch_it = registry.find(ArchiveEntry{format_id.c_str()});
if (arch_it != registry.end()) {
arch_from = arch_it;
arch_to = arch_it;
}
@ -77,52 +48,23 @@ std::unique_ptr<SLAArchiveReader> SLAArchiveReader::create(
if (ext.front() == '.')
ext.erase(ext.begin());
auto extcmp = [&ext](const auto &e) { return e == ext; };
for (auto it = arch_from; it != arch_to; ++it) {
const auto &[format_id, entry] = *it;
if (std::any_of(entry.extensions.begin(), entry.extensions.end(), extcmp))
ret = entry.factoryfn(fname, quality, progr);
for (auto it = arch_from; !ret && it != arch_to; ++it) {
const auto &entry = *it;
if (entry.rdfactoryfn) {
auto extensions = get_extensions(entry);
for (const std::string& supportedext : extensions) {
if (ext == supportedext) {
ret = entry.rdfactoryfn(fname, quality, progr);
break;
}
}
}
}
}
return ret;
}
const std::vector<const char *> &SLAArchiveReader::registered_archives()
{
static std::vector<const char*> archnames;
if (archnames.empty()) {
archnames.reserve(REGISTERED_ARCHIVES.size());
for (auto &[name, _] : REGISTERED_ARCHIVES)
archnames.emplace_back(name.c_str());
}
return archnames;
}
std::vector<const char *> SLAArchiveReader::get_extensions(const char *archtype)
{
auto it = REGISTERED_ARCHIVES.find(archtype);
if (it != REGISTERED_ARCHIVES.end())
return it->second.extensions;
return {};
}
const char *SLAArchiveReader::get_description(const char *archtype)
{
auto it = REGISTERED_ARCHIVES.find(archtype);
if (it != REGISTERED_ARCHIVES.end())
return it->second.descr;
return nullptr;
}
struct SliceParams { double layerh = 0., initial_layerh = 0.; };
static SliceParams get_slice_params(const DynamicPrintConfig &cfg)

View File

@ -47,15 +47,6 @@ public:
const std::string &format_id,
SLAImportQuality quality = SLAImportQuality::Balanced,
const ProgrFn &progr = [](int) { return false; });
// Get the names of currently known archive reader implementations
static const std::vector<const char *> & registered_archives();
// Get the understood file extensions belonging to an archive format
static std::vector<const char *> get_extensions(const char *archtype);
// Generic description (usable in GUI) about an archive format
static const char * get_description(const char *archtype);
};
// Raised in import_sla_archive when a nullptr reader is returned by

View File

@ -1,77 +1,18 @@
#include "SLAArchiveWriter.hpp"
#include "SL1.hpp"
#include "SL1_SVG.hpp"
#include "AnycubicSLA.hpp"
#include "libslic3r/libslic3r.h"
#include <string>
#include <map>
#include <memory>
#include <tuple>
#include "SLAArchiveFormatRegistry.hpp"
namespace Slic3r {
using ArchiveFactory = std::function<std::unique_ptr<SLAArchiveWriter>(const SLAPrinterConfig&)>;
struct ArchiveEntry {
const char *ext;
ArchiveFactory factoryfn;
};
static const std::map<std::string, ArchiveEntry> REGISTERED_ARCHIVES {
{
"SL1",
{ "sl1", [] (const auto &cfg) { return std::make_unique<SL1Archive>(cfg); } }
},
{
"SL1SVG",
{ "sl1_svg", [] (const auto &cfg) { return std::make_unique<SL1_SVGArchive>(cfg); } }
},
{
"SL2",
{ "sl1_svg", [] (const auto &cfg) { return std::make_unique<SL1_SVGArchive>(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>
SLAArchiveWriter::create(const std::string &archtype, const SLAPrinterConfig &cfg)
{
auto entry = REGISTERED_ARCHIVES.find(archtype);
std::unique_ptr<SLAArchiveWriter> ret;
auto factory = get_writer_factory(archtype.c_str());
if (entry != REGISTERED_ARCHIVES.end())
return entry->second.factoryfn(cfg);
if (factory)
ret = factory(cfg);
return nullptr;
}
const std::vector<const char*>& SLAArchiveWriter::registered_archives()
{
static std::vector<const char*> archnames;
if (archnames.empty()) {
archnames.reserve(REGISTERED_ARCHIVES.size());
for (auto &[name, _] : REGISTERED_ARCHIVES)
archnames.emplace_back(name.c_str());
}
return archnames;
}
const char *SLAArchiveWriter::get_extension(const char *archtype)
{
constexpr const char* DEFAULT_EXT = "zip";
auto entry = REGISTERED_ARCHIVES.find(archtype);
if (entry != REGISTERED_ARCHIVES.end())
return entry->second.ext;
return DEFAULT_EXT;
return ret;
}
} // namespace Slic3r

View File

@ -53,12 +53,6 @@ public:
// Factory method to create an archiver instance
static std::unique_ptr<SLAArchiveWriter> create(
const std::string &archtype, const SLAPrinterConfig &);
// Get the names of currently known archiver implementations
static const std::vector<const char *> & registered_archives();
// Get the default file extension belonging to an archive format
static const char *get_extension(const char *archtype);
};
} // namespace Slic3r

View File

@ -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)
@ -3030,7 +3036,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de
EXTRUDER_CONFIG(filament_max_volumetric_speed) / path.mm3_per_mm);
}
new_points = m_extrusion_quality_estimator.estimate_extrusion_quality(path, overhangs_with_speeds, overhang_w_fan_speeds,
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(),

View File

@ -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;
@ -292,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()) {
@ -306,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;
}

View File

@ -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' +

View File

@ -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;

View File

@ -437,7 +437,7 @@ Polygons extract_perimeter_polygons(const Layer *layer, std::vector<const LayerR
if (polygons.empty()) { // If there are no perimeter polygons for whatever reason (disabled perimeters .. ) insert dummy point
// 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);
}

View File

@ -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;

View File

@ -52,15 +52,15 @@ template bool contains(const ExPolygons &vector, const Point &point);
void simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval)
{
Polygons pp;
for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++it) {
Polygon p = *it;
p.points.push_back(p.points.front());
p.points = MultiPoint::_douglas_peucker(p.points, tolerance);
p.points.pop_back();
pp.push_back(p);
Polygons simplified_raw;
for (const Polygon &source_polygon : polygons) {
Points simplified = MultiPoint::douglas_peucker(to_polyline(source_polygon).points, tolerance);
if (simplified.size() > 3) {
simplified.pop_back();
simplified_raw.push_back(Polygon{ std::move(simplified) });
}
}
*retval = Slic3r::simplify_polygons(pp);
*retval = Slic3r::simplify_polygons(simplified_raw);
}
double linint(double value, double oldmin, double oldmax, double newmin, double newmax)

View File

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

View File

@ -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());

View File

@ -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

View File

@ -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.

View File

@ -230,10 +230,13 @@ Surfaces expand_bridges_detect_orientations(
bboxes[it - it_begin].overlap(bboxes[it2 - it_begin]) &&
// One may ignore holes, they are irrelevant for intersection test.
! intersection(it->expolygon.contour, it2->expolygon.contour).empty()) {
// The two bridge regions intersect. Give them the same group id.
// The two bridge regions intersect. Give them the same (lower) group id.
uint32_t id = group_id(it->src_id);
uint32_t id2 = group_id(it2->src_id);
bridges[it->src_id].group_id = bridges[it2->src_id].group_id = std::min(id, id2);
if (id < id2)
bridges[id2].group_id = id;
else
bridges[id].group_id = id2;
}
}
}

View File

@ -40,11 +40,12 @@ template<class L> auto get_b(L &&l) { return Traits<remove_cvref_t<L>>::get_b(l)
// Distance to the closest point of line.
template<class L>
double distance_to_squared(const L &line, const Vec<Dim<L>, Scalar<L>> &point, Vec<Dim<L>, Scalar<L>> *nearest_point)
inline double distance_to_squared(const L &line, const Vec<Dim<L>, Scalar<L>> &point, Vec<Dim<L>, Scalar<L>> *nearest_point)
{
const Vec<Dim<L>, double> v = (get_b(line) - get_a(line)).template cast<double>();
const Vec<Dim<L>, double> va = (point - get_a(line)).template cast<double>();
const double l2 = v.squaredNorm(); // avoid a sqrt
using VecType = Vec<Dim<L>, double>;
const VecType v = (get_b(line) - get_a(line)).template cast<double>();
const VecType va = (point - get_a(line)).template cast<double>();
const double l2 = v.squaredNorm();
if (l2 == 0.0) {
// a == b case
*nearest_point = get_a(line);
@ -53,19 +54,20 @@ double distance_to_squared(const L &line, const Vec<Dim<L>, Scalar<L>> &point, V
// Consider the line extending the segment, parameterized as a + t (b - a).
// We find projection of this point onto the line.
// It falls where t = [(this-a) . (b-a)] / |b-a|^2
const double t = va.dot(v) / l2;
const double t = va.dot(v);
if (t <= 0.0) {
// beyond the 'a' end of the segment
*nearest_point = get_a(line);
return va.squaredNorm();
} else if (t >= 1.0) {
} else if (t >= l2) {
// beyond the 'b' end of the segment
*nearest_point = get_b(line);
return (point - get_b(line)).template cast<double>().squaredNorm();
}
*nearest_point = (get_a(line).template cast<double>() + t * v).template cast<Scalar<L>>();
return (t * v - va).squaredNorm();
const VecType w = ((t / l2) * v).eval();
*nearest_point = (get_a(line).template cast<double>() + w).template cast<Scalar<L>>();
return (w - va).squaredNorm();
}
// Distance to the closest point of line.
@ -209,6 +211,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:

View File

@ -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;
}
@ -2408,12 +2405,8 @@ void ModelInstance::transform_polygon(Polygon* polygon) const
arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const
{
// static const double SIMPLIFY_TOLERANCE_MM = 0.1;
Vec3d rotation = get_rotation();
rotation.z() = 0.;
Transform3d trafo_instance = Geometry::assemble_transform(get_offset().z() * Vec3d::UnitZ(), rotation, get_scaling_factor(), get_mirror());
Polygon p = get_object()->convex_hull_2d(trafo_instance);
Polygon p = get_object()->convex_hull_2d(this->get_matrix());
// if (!p.points.empty()) {
// Polygons pp{p};
@ -2423,12 +2416,24 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const
arrangement::ArrangePolygon ret;
ret.poly.contour = std::move(p);
ret.translation = Vec2crd{scaled(get_offset(X)), scaled(get_offset(Y))};
ret.rotation = get_rotation(Z);
ret.translation = Vec2crd::Zero();
ret.rotation = 0.;
return ret;
}
void ModelInstance::apply_arrange_result(const Vec2d &offs, double rotation)
{
// write the transformation data into the model instance
auto trafo = get_transformation().get_matrix();
auto tr = Transform3d::Identity();
tr.translate(to_3d(unscaled(offs), 0.));
trafo = tr * Eigen::AngleAxisd(rotation, Vec3d::UnitZ()) * trafo;
m_transformation.set_matrix(trafo);
this->object->invalidate_bounding_box();
}
indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, EnforcerBlockerType type) const
{
TriangleSelector selector(mv.mesh());

View File

@ -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();
@ -1167,14 +1167,7 @@ public:
arrangement::ArrangePolygon get_arrange_polygon() const;
// Apply the arrange result on the ModelInstance
void apply_arrange_result(const Vec2d& offs, double rotation)
{
// write the transformation data into the model instance
set_rotation(Z, rotation);
set_offset(X, unscale<double>(offs(X)));
set_offset(Y, unscale<double>(offs(Y)));
this->object->invalidate_bounding_box();
}
void apply_arrange_result(const Vec2d& offs, double rotation);
protected:
friend class Print;

View File

@ -103,10 +103,10 @@ 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;
double tolerance_sq = tolerance * tolerance;
Points result_pts;
auto tolerance_sq = int64_t(sqr(tolerance));
if (! pts.empty()) {
const Point *anchor = &pts.front();
size_t anchor_idx = 0;
@ -120,14 +120,40 @@ std::vector<Point> MultiPoint::_douglas_peucker(const std::vector<Point>& pts, c
dpStack.reserve(pts.size());
dpStack.emplace_back(floater_idx);
for (;;) {
double max_dist_sq = 0.0;
size_t furthest_idx = anchor_idx;
int64_t max_dist_sq = 0;
size_t furthest_idx = anchor_idx;
// find point furthest from line seg created by (anchor, floater) and note it
for (size_t i = anchor_idx + 1; i < floater_idx; ++ i) {
double dist_sq = Line::distance_to_squared(pts[i], *anchor, *floater);
if (dist_sq > max_dist_sq) {
max_dist_sq = dist_sq;
furthest_idx = i;
{
const Point a = *anchor;
const Point f = *floater;
const Vec2i64 v = (f - a).cast<int64_t>();
if (const int64_t l2 = v.squaredNorm(); l2 == 0) {
for (size_t i = anchor_idx + 1; i < floater_idx; ++ i)
if (int64_t dist_sq = (pts[i] - a).cast<int64_t>().squaredNorm(); dist_sq > max_dist_sq) {
max_dist_sq = dist_sq;
furthest_idx = i;
}
} else {
const double dl2 = double(l2);
const Vec2d dv = v.cast<double>();
for (size_t i = anchor_idx + 1; i < floater_idx; ++ i) {
const Point p = pts[i];
const Vec2i64 va = (p - a).template cast<int64_t>();
const int64_t t = va.dot(v);
int64_t dist_sq;
if (t <= 0) {
dist_sq = va.squaredNorm();
} else if (t >= l2) {
dist_sq = (p - f).cast<int64_t>().squaredNorm();
} else {
const Vec2i64 w = ((double(t) / dl2) * dv).cast<int64_t>();
dist_sq = (w - va).squaredNorm();
}
if (dist_sq > max_dist_sq) {
max_dist_sq = dist_sq;
furthest_idx = i;
}
}
}
}
// remove point if less than tolerance

View File

@ -81,7 +81,7 @@ public:
}
}
static Points _douglas_peucker(const Points &points, const double tolerance);
static Points douglas_peucker(const Points &points, const double tolerance);
static Points visivalingam(const Points& pts, const double& tolerance);
inline auto begin() { return points.begin(); }
@ -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) {

View File

@ -40,10 +40,11 @@
#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 +570,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;
@ -1153,11 +1154,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];

View File

@ -1644,9 +1644,9 @@ namespace client
}
if (! evaluated) {
// Clamp x into the table range with EPSILON.
if (x > table.table.front().x - EPSILON)
if (double x0 = table.table.front().x; x > x0 - EPSILON && x < x0)
out.set_d(table.table.front().y);
else if (x < table.table.back().x + EPSILON)
else if (double x1 = table.table.back().x; x > x1 && x < x1 + EPSILON)
out.set_d(table.table.back().y);
else
// The value is really outside the table range.

View File

@ -57,7 +57,7 @@ void Point::rotate(double angle, const Point &center)
(*this)(1) = (coord_t)round( (double)center(1) + c * dy + s * dx );
}
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)
{

View File

@ -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());
}

View File

@ -4,6 +4,8 @@
#include "Polygon.hpp"
#include "Polyline.hpp"
#include <ankerl/unordered_dense.h>
namespace Slic3r {
double Polygon::length() const
@ -94,7 +96,7 @@ bool Polygon::make_clockwise()
void Polygon::douglas_peucker(double tolerance)
{
this->points.push_back(this->points.front());
Points p = MultiPoint::_douglas_peucker(this->points, tolerance);
Points p = MultiPoint::douglas_peucker(this->points, tolerance);
p.pop_back();
this->points = std::move(p);
}
@ -108,7 +110,7 @@ Polygons Polygon::simplify(double tolerance) const
// on the whole polygon
Points points = this->points;
points.push_back(points.front());
Polygon p(MultiPoint::_douglas_peucker(points, tolerance));
Polygon p(MultiPoint::douglas_peucker(points, tolerance));
p.points.pop_back();
Polygons pp;
@ -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)
@ -557,23 +577,40 @@ void remove_collinear(Polygons &polys)
remove_collinear(poly);
}
Polygons polygons_simplify(const Polygons &source_polygons, double tolerance)
static inline void simplify_polygon_impl(const Points &points, double tolerance, bool strictly_simple, Polygons &out)
{
Points simplified = MultiPoint::douglas_peucker(points, tolerance);
// then remove the last (repeated) point.
simplified.pop_back();
// Simplify the decimated contour by ClipperLib.
bool ccw = ClipperLib::Area(simplified) > 0.;
for (Points& path : ClipperLib::SimplifyPolygons(ClipperUtils::SinglePathProvider(simplified), ClipperLib::pftNonZero, strictly_simple)) {
if (!ccw)
// ClipperLib likely reoriented negative area contours to become positive. Reverse holes back to CW.
std::reverse(path.begin(), path.end());
out.emplace_back(std::move(path));
}
}
Polygons polygons_simplify(Polygons &&source_polygons, double tolerance, bool strictly_simple /* = true */)
{
Polygons out;
out.reserve(source_polygons.size());
for (Polygon &source_polygon : source_polygons) {
// Run Douglas / Peucker simplification algorithm on an open polyline (by repeating the first point at the end of the polyline),
source_polygon.points.emplace_back(source_polygon.points.front());
simplify_polygon_impl(source_polygon.points, tolerance, strictly_simple, out);
}
return out;
}
Polygons polygons_simplify(const Polygons &source_polygons, double tolerance, bool strictly_simple /* = true */)
{
Polygons out;
out.reserve(source_polygons.size());
for (const Polygon &source_polygon : source_polygons) {
// Run Douglas / Peucker simplification algorithm on an open polyline (by repeating the first point at the end of the polyline),
Points simplified = MultiPoint::_douglas_peucker(to_polyline(source_polygon).points, tolerance);
// then remove the last (repeated) point.
simplified.pop_back();
// Simplify the decimated contour by ClipperLib.
bool ccw = ClipperLib::Area(simplified) > 0.;
for (Points &path : ClipperLib::SimplifyPolygons(ClipperUtils::SinglePathProvider(simplified), ClipperLib::pftNonZero)) {
if (! ccw)
// ClipperLib likely reoriented negative area contours to become positive. Reverse holes back to CW.
std::reverse(path.begin(), path.end());
out.emplace_back(std::move(path));
}
simplify_polygon_impl(to_polyline(source_polygon).points, tolerance, strictly_simple, out);
}
return out;
}

View File

@ -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);
@ -148,7 +149,8 @@ inline void polygons_append(Polygons &dst, Polygons &&src)
}
}
Polygons polygons_simplify(const Polygons &polys, double tolerance);
Polygons polygons_simplify(Polygons &&polys, double tolerance, bool strictly_simple = true);
Polygons polygons_simplify(const Polygons &polys, double tolerance, bool strictly_simple = true);
inline void polygons_rotate(Polygons &polys, double angle)
{
@ -241,7 +243,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 +252,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());

View File

@ -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;

View File

@ -110,7 +110,7 @@ Points Polyline::equally_spaced_points(double distance) const
void Polyline::simplify(double tolerance)
{
this->points = MultiPoint::_douglas_peucker(this->points, tolerance);
this->points = MultiPoint::douglas_peucker(this->points, tolerance);
}
#if 0

View File

@ -158,8 +158,8 @@ inline void polylines_append(Polylines &dst, Polylines &&src)
// src_first: the merge point is at src.begin() or src.end()?
// 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)

View File

@ -444,7 +444,8 @@ static std::vector<std::string> s_Preset_print_options {
"support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops",
"support_material_contact_distance", "support_material_bottom_contact_distance",
"support_material_buildplate_only",
"support_tree_angle", "support_tree_angle_slow", "support_tree_branch_diameter", "support_tree_branch_diameter_angle", "support_tree_top_rate", "support_tree_branch_distance", "support_tree_tip_diameter",
"support_tree_angle", "support_tree_angle_slow", "support_tree_branch_diameter", "support_tree_branch_diameter_angle", "support_tree_branch_diameter_double_wall",
"support_tree_top_rate", "support_tree_branch_distance", "support_tree_tip_diameter",
"dont_support_bridges", "thick_bridges", "notes", "complete_objects", "extruder_clearance_radius",
"extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "gcode_substitutions", "perimeter_extruder",
"infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder",

View File

@ -8,7 +8,6 @@
#include "Geometry/ConvexHull.hpp"
#include "I18N.hpp"
#include "ShortestPath.hpp"
#include "SupportMaterial.hpp"
#include "Thread.hpp"
#include "GCode.hpp"
#include "GCode/WipeTower.hpp"
@ -520,8 +519,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.");
}
}
@ -878,7 +881,6 @@ void Print::process()
BOOST_LOG_TRIVIAL(info) << "Starting the slicing process." << log_memory_info();
for (PrintObject *obj : m_objects)
obj->make_perimeters();
this->set_status(70, _u8L("Infilling layers"));
for (PrintObject *obj : m_objects)
obj->infill();
for (PrintObject *obj : m_objects)
@ -1130,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;

View File

@ -627,7 +627,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.
@ -664,6 +664,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;
};

View File

@ -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));
@ -2937,6 +2939,18 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(5));
def = this->add("support_tree_branch_diameter_double_wall", coFloat);
def->label = L("Branch Diameter with double walls");
def->category = L("Support material");
// TRN PrintSettings: "Organic supports" > "Branch Diameter"
def->tooltip = L("Branches with area larger than the area of a circle of this diameter will be printed with double walls for stability. "
"Set this value to zero for no double walls.");
def->sidetext = L("mm");
def->min = 0;
def->max = 100.f;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(3));
// Tree Support Branch Distance
// How far apart the branches need to be when they touch the model. Making this distance small will cause
// the tree support to touch the model at more points, causing better overhang but making support harder to remove.

View File

@ -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 {
@ -554,6 +554,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloat, support_tree_angle_slow))
((ConfigOptionFloat, support_tree_branch_diameter))
((ConfigOptionFloat, support_tree_branch_diameter_angle))
((ConfigOptionFloat, support_tree_branch_diameter_double_wall))
((ConfigOptionPercent, support_tree_top_rate))
((ConfigOptionFloat, support_tree_branch_distance))
((ConfigOptionFloat, support_tree_tip_diameter))

View File

@ -17,8 +17,8 @@
#include "MutablePolygon.hpp"
#include "PrintBase.hpp"
#include "PrintConfig.hpp"
#include "SupportMaterial.hpp"
#include "TreeSupport.hpp"
#include "Support/SupportMaterial.hpp"
#include "Support/TreeSupport.hpp"
#include "Surface.hpp"
#include "Slicing.hpp"
#include "Tesselate.hpp"
@ -27,7 +27,7 @@
#include "Fill/FillAdaptive.hpp"
#include "Fill/FillLightning.hpp"
#include "Format/STL.hpp"
#include "SupportMaterial.hpp"
#include "Support/SupportMaterial.hpp"
#include "SupportSpotsGenerator.hpp"
#include "TriangleSelectorWrapper.hpp"
#include "format.hpp"
@ -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, &region, region_id](const tbb::blocked_range<size_t>& range) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
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]));
@ -691,6 +712,7 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "support_tree_angle_slow"
|| opt_key == "support_tree_branch_diameter"
|| opt_key == "support_tree_branch_diameter_angle"
|| opt_key == "support_tree_branch_diameter_double_wall"
|| opt_key == "support_tree_top_rate"
|| opt_key == "support_tree_branch_distance"
|| opt_key == "support_tree_tip_diameter"
@ -909,6 +931,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 +1080,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 +1139,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 +1165,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;
@ -1192,6 +1218,7 @@ void PrintObject::discover_vertical_shells()
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) {
@ -1265,6 +1292,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();
@ -1290,10 +1318,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();
@ -1603,21 +1633,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;
@ -1627,6 +1654,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) {
@ -1677,7 +1705,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)),
@ -1724,6 +1752,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(
@ -1755,6 +1784,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)) {
@ -1993,8 +2023,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;
@ -2073,6 +2103,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];
@ -2135,6 +2166,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()) {
@ -2142,6 +2174,10 @@ 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);
@ -2196,7 +2232,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);
@ -2229,7 +2265,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();
@ -2240,6 +2276,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;
@ -2756,6 +2793,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.

View File

@ -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); });

View File

@ -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 {

View File

@ -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

View File

@ -305,7 +305,7 @@ void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep st
bench.stop();
if (!m.empty())
if (!po.m_preview_meshes[step]->empty())
BOOST_LOG_TRIVIAL(trace) << "Preview gen took: " << bench.getElapsedSec();
else
BOOST_LOG_TRIVIAL(error) << "Preview failed!";

View File

@ -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());

View File

@ -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.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,155 @@
#ifndef slic3r_SupportCommon_hpp_
#define slic3r_SupportCommon_hpp_
#include "../Polygon.hpp"
#include "SupportLayer.hpp"
#include "SupportParameters.hpp"
namespace Slic3r {
class PrintObject;
class SupportLayer;
namespace FFFSupport {
// Remove bridges from support contact areas.
// To be called if PrintObjectConfig::dont_support_bridges.
void remove_bridges_from_contacts(
const PrintConfig &print_config,
const Layer &lower_layer,
const LayerRegion &layerm,
float fw,
Polygons &contact_polygons);
// Turn some of the base layers into base interface layers.
// For soluble interfaces with non-soluble bases, print maximum two first interface layers with the base
// extruder to improve adhesion of the soluble filament to the base.
// For Organic supports, merge top_interface_layers & top_base_interface_layers with the interfaces
// produced by this function.
std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interface_layers(
const PrintObjectConfig &config,
const SupportParameters &support_params,
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
// Input / output, will be merged with output
SupportGeneratorLayersPtr &top_interface_layers,
SupportGeneratorLayersPtr &top_base_interface_layers,
// Input, will be trimmed with the newly created interface layers.
SupportGeneratorLayersPtr &intermediate_layers,
SupportGeneratorLayerStorage &layer_storage);
// Generate raft layers, also expand the 1st support layer
// in case there is no raft layer to improve support adhesion.
SupportGeneratorLayersPtr generate_raft_base(
const PrintObject &object,
const SupportParameters &support_params,
const SlicingParameters &slicing_params,
const SupportGeneratorLayersPtr &top_contacts,
const SupportGeneratorLayersPtr &interface_layers,
const SupportGeneratorLayersPtr &base_interface_layers,
const SupportGeneratorLayersPtr &base_layers,
SupportGeneratorLayerStorage &layer_storage);
// returns sorted layers
SupportGeneratorLayersPtr generate_support_layers(
PrintObject &object,
const SupportGeneratorLayersPtr &raft_layers,
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
const SupportGeneratorLayersPtr &intermediate_layers,
const SupportGeneratorLayersPtr &interface_layers,
const SupportGeneratorLayersPtr &base_interface_layers);
// Produce the support G-code.
// Used by both classic and tree supports.
void generate_support_toolpaths(
SupportLayerPtrs &support_layers,
const PrintObjectConfig &config,
const SupportParameters &support_params,
const SlicingParameters &slicing_params,
const SupportGeneratorLayersPtr &raft_layers,
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
const SupportGeneratorLayersPtr &intermediate_layers,
const SupportGeneratorLayersPtr &interface_layers,
const SupportGeneratorLayersPtr &base_interface_layers);
// FN_HIGHER_EQUAL: the provided object pointer has a Z value >= of an internal threshold.
// Find the first item with Z value >= of an internal threshold of fn_higher_equal.
// If no vec item with Z value >= of an internal threshold of fn_higher_equal is found, return vec.size()
// If the initial idx is size_t(-1), then use binary search.
// Otherwise search linearly upwards.
template<typename IteratorType, typename IndexType, typename FN_HIGHER_EQUAL>
IndexType idx_higher_or_equal(IteratorType begin, IteratorType end, IndexType idx, FN_HIGHER_EQUAL fn_higher_equal)
{
auto size = int(end - begin);
if (size == 0) {
idx = 0;
} else if (idx == IndexType(-1)) {
// First of the batch of layers per thread pool invocation. Use binary search.
int idx_low = 0;
int idx_high = std::max(0, size - 1);
while (idx_low + 1 < idx_high) {
int idx_mid = (idx_low + idx_high) / 2;
if (fn_higher_equal(begin[idx_mid]))
idx_high = idx_mid;
else
idx_low = idx_mid;
}
idx = fn_higher_equal(begin[idx_low]) ? idx_low :
(fn_higher_equal(begin[idx_high]) ? idx_high : size);
} else {
// For the other layers of this batch of layers, search incrementally, which is cheaper than the binary search.
while (int(idx) < size && ! fn_higher_equal(begin[idx]))
++ idx;
}
return idx;
}
template<typename T, typename IndexType, typename FN_HIGHER_EQUAL>
IndexType idx_higher_or_equal(const std::vector<T>& vec, IndexType idx, FN_HIGHER_EQUAL fn_higher_equal)
{
return idx_higher_or_equal(vec.begin(), vec.end(), idx, fn_higher_equal);
}
// FN_LOWER_EQUAL: the provided object pointer has a Z value <= of an internal threshold.
// Find the first item with Z value <= of an internal threshold of fn_lower_equal.
// If no vec item with Z value <= of an internal threshold of fn_lower_equal is found, return -1.
// If the initial idx is < -1, then use binary search.
// Otherwise search linearly downwards.
template<typename IT, typename FN_LOWER_EQUAL>
int idx_lower_or_equal(IT begin, IT end, int idx, FN_LOWER_EQUAL fn_lower_equal)
{
auto size = int(end - begin);
if (size == 0) {
idx = -1;
} else if (idx < -1) {
// First of the batch of layers per thread pool invocation. Use binary search.
int idx_low = 0;
int idx_high = std::max(0, size - 1);
while (idx_low + 1 < idx_high) {
int idx_mid = (idx_low + idx_high) / 2;
if (fn_lower_equal(begin[idx_mid]))
idx_low = idx_mid;
else
idx_high = idx_mid;
}
idx = fn_lower_equal(begin[idx_high]) ? idx_high :
(fn_lower_equal(begin[idx_low ]) ? idx_low : -1);
} else {
// For the other layers of this batch of layers, search incrementally, which is cheaper than the binary search.
while (idx >= 0 && ! fn_lower_equal(begin[idx]))
-- idx;
}
return idx;
}
template<typename T, typename FN_LOWER_EQUAL>
int idx_lower_or_equal(const std::vector<T*> &vec, int idx, FN_LOWER_EQUAL fn_lower_equal)
{
return idx_lower_or_equal(vec.begin(), vec.end(), idx, fn_lower_equal);
}
} // namespace FFFSupport
} // namespace Slic3r
#endif /* slic3r_SupportCommon_hpp_ */

View File

@ -0,0 +1,108 @@
#if 1 //#ifdef SLIC3R_DEBUG
#include "../ClipperUtils.hpp"
#include "../SVG.hpp"
#include "../Layer.hpp"
#include "SupportLayer.hpp"
namespace Slic3r::FFFSupport {
const char* support_surface_type_to_color_name(const SupporLayerType surface_type)
{
switch (surface_type) {
case SupporLayerType::TopContact: return "rgb(255,0,0)"; // "red";
case SupporLayerType::TopInterface: return "rgb(0,255,0)"; // "green";
case SupporLayerType::Base: return "rgb(0,0,255)"; // "blue";
case SupporLayerType::BottomInterface:return "rgb(255,255,128)"; // yellow
case SupporLayerType::BottomContact: return "rgb(255,0,255)"; // magenta
case SupporLayerType::RaftInterface: return "rgb(0,255,255)";
case SupporLayerType::RaftBase: return "rgb(128,128,128)";
case SupporLayerType::Unknown: return "rgb(128,0,0)"; // maroon
default: return "rgb(64,64,64)";
};
}
Point export_support_surface_type_legend_to_svg_box_size()
{
return Point(scale_(1.+10.*8.), scale_(3.));
}
void export_support_surface_type_legend_to_svg(SVG &svg, const Point &pos)
{
// 1st row
coord_t pos_x0 = pos(0) + scale_(1.);
coord_t pos_x = pos_x0;
coord_t pos_y = pos(1) + scale_(1.5);
coord_t step_x = scale_(10.);
svg.draw_legend(Point(pos_x, pos_y), "top contact" , support_surface_type_to_color_name(SupporLayerType::TopContact));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "top iface" , support_surface_type_to_color_name(SupporLayerType::TopInterface));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "base" , support_surface_type_to_color_name(SupporLayerType::Base));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "bottom iface" , support_surface_type_to_color_name(SupporLayerType::BottomInterface));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "bottom contact" , support_surface_type_to_color_name(SupporLayerType::BottomContact));
// 2nd row
pos_x = pos_x0;
pos_y = pos(1)+scale_(2.8);
svg.draw_legend(Point(pos_x, pos_y), "raft interface" , support_surface_type_to_color_name(SupporLayerType::RaftInterface));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "raft base" , support_surface_type_to_color_name(SupporLayerType::RaftBase));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "unknown" , support_surface_type_to_color_name(SupporLayerType::Unknown));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "intermediate" , support_surface_type_to_color_name(SupporLayerType::Intermediate));
}
void export_print_z_polygons_to_svg(const char *path, SupportGeneratorLayer ** const layers, int n_layers)
{
BoundingBox bbox;
for (int i = 0; i < n_layers; ++ i)
bbox.merge(get_extents(layers[i]->polygons));
Point legend_size = export_support_surface_type_legend_to_svg_box_size();
Point legend_pos(bbox.min(0), bbox.max(1));
bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1)));
SVG svg(path, bbox);
const float transparency = 0.5f;
for (int i = 0; i < n_layers; ++ i)
svg.draw(union_ex(layers[i]->polygons), support_surface_type_to_color_name(layers[i]->layer_type), transparency);
for (int i = 0; i < n_layers; ++ i)
svg.draw(to_polylines(layers[i]->polygons), support_surface_type_to_color_name(layers[i]->layer_type));
export_support_surface_type_legend_to_svg(svg, legend_pos);
svg.Close();
}
void export_print_z_polygons_and_extrusions_to_svg(
const char *path,
SupportGeneratorLayer ** const layers,
int n_layers,
SupportLayer &support_layer)
{
BoundingBox bbox;
for (int i = 0; i < n_layers; ++ i)
bbox.merge(get_extents(layers[i]->polygons));
Point legend_size = export_support_surface_type_legend_to_svg_box_size();
Point legend_pos(bbox.min(0), bbox.max(1));
bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1)));
SVG svg(path, bbox);
const float transparency = 0.5f;
for (int i = 0; i < n_layers; ++ i)
svg.draw(union_ex(layers[i]->polygons), support_surface_type_to_color_name(layers[i]->layer_type), transparency);
for (int i = 0; i < n_layers; ++ i)
svg.draw(to_polylines(layers[i]->polygons), support_surface_type_to_color_name(layers[i]->layer_type));
Polygons polygons_support, polygons_interface;
support_layer.support_fills.polygons_covered_by_width(polygons_support, float(SCALED_EPSILON));
// support_layer.support_interface_fills.polygons_covered_by_width(polygons_interface, SCALED_EPSILON);
svg.draw(union_ex(polygons_support), "brown");
svg.draw(union_ex(polygons_interface), "black");
export_support_surface_type_legend_to_svg(svg, legend_pos);
svg.Close();
}
} // namespace Slic3r
#endif /* SLIC3R_DEBUG */

View File

@ -0,0 +1,18 @@
#ifndef slic3r_SupportCommon_hpp_
#define slic3r_SupportCommon_hpp_
namespace Slic3r {
class SupportGeneratorLayer;
class SupportLayer;
namespace FFFSupport {
void export_print_z_polygons_to_svg(const char *path, SupportGeneratorLayer ** const layers, size_t n_layers);
void export_print_z_polygons_and_extrusions_to_svg(const char *path, SupportGeneratorLayer ** const layers, size_t n_layers, SupportLayer& support_layer);
} // namespace FFFSupport
} // namespace Slic3r
#endif /* slic3r_SupportCommon_hpp_ */

View File

@ -0,0 +1,146 @@
#ifndef slic3r_SupportLayer_hpp_
#define slic3r_SupportLayer_hpp_
#include <oneapi/tbb/scalable_allocator.h>
#include <oneapi/tbb/spin_mutex.h>
// for Slic3r::deque
#include "../libslic3r.h"
#include "ClipperUtils.hpp"
#include "Polygon.hpp"
namespace Slic3r::FFFSupport {
// Support layer type to be used by SupportGeneratorLayer. This type carries a much more detailed information
// about the support layer type than the final support layers stored in a PrintObject.
enum class SupporLayerType {
Unknown = 0,
// Ratft base layer, to be printed with the support material.
RaftBase,
// Raft interface layer, to be printed with the support interface material.
RaftInterface,
// Bottom contact layer placed over a top surface of an object. To be printed with a support interface material.
BottomContact,
// Dense interface layer, to be printed with the support interface material.
// This layer is separated from an object by an BottomContact layer.
BottomInterface,
// Sparse base support layer, to be printed with a support material.
Base,
// Dense interface layer, to be printed with the support interface material.
// This layer is separated from an object with TopContact layer.
TopInterface,
// Top contact layer directly supporting an overhang. To be printed with a support interface material.
TopContact,
// Some undecided type yet. It will turn into Base first, then it may turn into BottomInterface or TopInterface.
Intermediate,
};
// A support layer type used internally by the SupportMaterial class. This class carries a much more detailed
// information about the support layer than the layers stored in the PrintObject, mainly
// the SupportGeneratorLayer is aware of the bridging flow and the interface gaps between the object and the support.
class SupportGeneratorLayer
{
public:
void reset() {
*this = SupportGeneratorLayer();
}
bool operator==(const SupportGeneratorLayer &layer2) const {
return print_z == layer2.print_z && height == layer2.height && bridging == layer2.bridging;
}
// Order the layers by lexicographically by an increasing print_z and a decreasing layer height.
bool operator<(const SupportGeneratorLayer &layer2) const {
if (print_z < layer2.print_z) {
return true;
} else if (print_z == layer2.print_z) {
if (height > layer2.height)
return true;
else if (height == layer2.height) {
// Bridging layers first.
return bridging && ! layer2.bridging;
} else
return false;
} else
return false;
}
void merge(SupportGeneratorLayer &&rhs) {
// The union_() does not support move semantic yet, but maybe one day it will.
this->polygons = union_(this->polygons, std::move(rhs.polygons));
auto merge = [](std::unique_ptr<Polygons> &dst, std::unique_ptr<Polygons> &src) {
if (! dst || dst->empty())
dst = std::move(src);
else if (src && ! src->empty())
*dst = union_(*dst, std::move(*src));
};
merge(this->contact_polygons, rhs.contact_polygons);
merge(this->overhang_polygons, rhs.overhang_polygons);
merge(this->enforcer_polygons, rhs.enforcer_polygons);
rhs.reset();
}
// For the bridging flow, bottom_print_z will be above bottom_z to account for the vertical separation.
// For the non-bridging flow, bottom_print_z will be equal to bottom_z.
coordf_t bottom_print_z() const { return print_z - height; }
// To sort the extremes of top / bottom interface layers.
coordf_t extreme_z() const { return (this->layer_type == SupporLayerType::TopContact) ? this->bottom_z : this->print_z; }
SupporLayerType layer_type { SupporLayerType::Unknown };
// Z used for printing, in unscaled coordinates.
coordf_t print_z { 0 };
// Bottom Z of this layer. For soluble layers, bottom_z + height = print_z,
// otherwise bottom_z + gap + height = print_z.
coordf_t bottom_z { 0 };
// Layer height in unscaled coordinates.
coordf_t height { 0 };
// Index of a PrintObject layer_id supported by this layer. This will be set for top contact layers.
// If this is not a contact layer, it will be set to size_t(-1).
size_t idx_object_layer_above { size_t(-1) };
// Index of a PrintObject layer_id, which supports this layer. This will be set for bottom contact layers.
// If this is not a contact layer, it will be set to size_t(-1).
size_t idx_object_layer_below { size_t(-1) };
// Use a bridging flow when printing this support layer.
bool bridging { false };
// Polygons to be filled by the support pattern.
Polygons polygons;
// Currently for the contact layers only.
std::unique_ptr<Polygons> contact_polygons;
std::unique_ptr<Polygons> overhang_polygons;
// Enforcers need to be propagated independently in case the "support on build plate only" option is enabled.
std::unique_ptr<Polygons> enforcer_polygons;
};
// Layers are allocated and owned by a deque. Once a layer is allocated, it is maintained
// up to the end of a generate() method. The layer storage may be replaced by an allocator class in the future,
// which would allocate layers by multiple chunks.
class SupportGeneratorLayerStorage {
public:
SupportGeneratorLayer& allocate_unguarded(SupporLayerType layer_type) {
m_storage.emplace_back();
m_storage.back().layer_type = layer_type;
return m_storage.back();
}
SupportGeneratorLayer& allocate(SupporLayerType layer_type)
{
m_mutex.lock();
m_storage.emplace_back();
SupportGeneratorLayer *layer_new = &m_storage.back();
m_mutex.unlock();
layer_new->layer_type = layer_type;
return *layer_new;
}
private:
template<typename BaseType>
using Allocator = tbb::scalable_allocator<BaseType>;
Slic3r::deque<SupportGeneratorLayer, Allocator<SupportGeneratorLayer>> m_storage;
tbb::spin_mutex m_mutex;
};
using SupportGeneratorLayersPtr = std::vector<SupportGeneratorLayer*>;
} // namespace Slic3r
#endif /* slic3r_SupportLayer_hpp_ */

View File

@ -0,0 +1,101 @@
#ifndef slic3r_SupportMaterial_hpp_
#define slic3r_SupportMaterial_hpp_
#include "../Flow.hpp"
#include "../PrintConfig.hpp"
#include "../Slicing.hpp"
#include "SupportLayer.hpp"
#include "SupportParameters.hpp"
namespace Slic3r {
class PrintObject;
// This class manages raft and supports for a single PrintObject.
// Instantiated by Slic3r::Print::Object->_support_material()
// This class is instantiated before the slicing starts as Object.pm will query
// the parameters of the raft to determine the 1st layer height and thickness.
class PrintObjectSupportMaterial
{
public:
PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params);
// Is raft enabled?
bool has_raft() const { return m_slicing_params.has_raft(); }
// Has any support?
bool has_support() const { return m_object_config->support_material.value || m_object_config->support_material_enforce_layers; }
bool build_plate_only() const { return this->has_support() && m_object_config->support_material_buildplate_only.value; }
bool synchronize_layers() const { return m_slicing_params.soluble_interface && m_object_config->support_material_synchronize_layers.value; }
bool has_contact_loops() const { return m_object_config->support_material_interface_contact_loops.value; }
// Generate support material for the object.
// New support layers will be added to the object,
// with extrusion paths and islands filled in for each support layer.
void generate(PrintObject &object);
private:
using SupportGeneratorLayersPtr = FFFSupport::SupportGeneratorLayersPtr;
using SupportGeneratorLayerStorage = FFFSupport::SupportGeneratorLayerStorage;
using SupportParameters = FFFSupport::SupportParameters;
std::vector<Polygons> buildplate_covered(const PrintObject &object) const;
// Generate top contact layers supporting overhangs.
// For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined.
// If supports over bed surface only are requested, don't generate contact layers over an object.
SupportGeneratorLayersPtr top_contact_layers(const PrintObject &object, const std::vector<Polygons> &buildplate_covered, SupportGeneratorLayerStorage &layer_storage) const;
// Generate bottom contact layers supporting the top contact layers.
// For a soluble interface material synchronize the layer heights with the object,
// otherwise set the layer height to a bridging flow of a support interface nozzle.
SupportGeneratorLayersPtr bottom_contact_layers_and_layer_support_areas(
const PrintObject &object, const SupportGeneratorLayersPtr &top_contacts, std::vector<Polygons> &buildplate_covered,
SupportGeneratorLayerStorage &layer_storage, std::vector<Polygons> &layer_support_areas) const;
// Trim the top_contacts layers with the bottom_contacts layers if they overlap, so there would not be enough vertical space for both of them.
void trim_top_contacts_by_bottom_contacts(const PrintObject &object, const SupportGeneratorLayersPtr &bottom_contacts, SupportGeneratorLayersPtr &top_contacts) const;
// Generate raft layers and the intermediate support layers between the bottom contact and top contact surfaces.
SupportGeneratorLayersPtr raft_and_intermediate_support_layers(
const PrintObject &object,
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
SupportGeneratorLayerStorage &layer_storage) const;
// Fill in the base layers with polygons.
void generate_base_layers(
const PrintObject &object,
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
SupportGeneratorLayersPtr &intermediate_layers,
const std::vector<Polygons> &layer_support_areas) const;
// Trim support layers by an object to leave a defined gap between
// the support volume and the object.
void trim_support_layers_by_object(
const PrintObject &object,
SupportGeneratorLayersPtr &support_layers,
const coordf_t gap_extra_above,
const coordf_t gap_extra_below,
const coordf_t gap_xy) const;
/*
void generate_pillars_shape();
void clip_with_shape();
*/
// Following objects are not owned by SupportMaterial class.
const PrintConfig *m_print_config;
const PrintObjectConfig *m_object_config;
// Pre-calculated parameters shared between the object slicer and the support generator,
// carrying information on a raft, 1st layer height, 1st object layer height, gap between the raft and object etc.
SlicingParameters m_slicing_params;
// Various precomputed support parameters to be shared with external functions.
SupportParameters m_support_params;
};
} // namespace Slic3r
#endif /* slic3r_SupportMaterial_hpp_ */

View File

@ -0,0 +1,144 @@
#include "../Print.hpp"
#include "../PrintConfig.hpp"
#include "../Slicing.hpp"
#include "SupportParameters.hpp"
namespace Slic3r::FFFSupport {
SupportParameters::SupportParameters(const PrintObject &object)
{
const PrintConfig &print_config = object.print()->config();
const PrintObjectConfig &object_config = object.config();
const SlicingParameters &slicing_params = object.slicing_parameters();
this->soluble_interface = slicing_params.soluble_interface;
this->soluble_interface_non_soluble_base =
// Zero z-gap between the overhangs and the support interface.
slicing_params.soluble_interface &&
// Interface extruder soluble.
object_config.support_material_interface_extruder.value > 0 && print_config.filament_soluble.get_at(object_config.support_material_interface_extruder.value - 1) &&
// Base extruder: Either "print with active extruder" not soluble.
(object_config.support_material_extruder.value == 0 || ! print_config.filament_soluble.get_at(object_config.support_material_extruder.value - 1));
{
int num_top_interface_layers = std::max(0, object_config.support_material_interface_layers.value);
int num_bottom_interface_layers = object_config.support_material_bottom_interface_layers < 0 ?
num_top_interface_layers : object_config.support_material_bottom_interface_layers;
this->has_top_contacts = num_top_interface_layers > 0;
this->has_bottom_contacts = num_bottom_interface_layers > 0;
this->num_top_interface_layers = this->has_top_contacts ? size_t(num_top_interface_layers - 1) : 0;
this->num_bottom_interface_layers = this->has_bottom_contacts ? size_t(num_bottom_interface_layers - 1) : 0;
if (this->soluble_interface_non_soluble_base) {
// Try to support soluble dense interfaces with non-soluble dense interfaces.
this->num_top_base_interface_layers = size_t(std::min(num_top_interface_layers / 2, 2));
this->num_bottom_base_interface_layers = size_t(std::min(num_bottom_interface_layers / 2, 2));
} else {
this->num_top_base_interface_layers = 0;
this->num_bottom_base_interface_layers = 0;
}
}
this->first_layer_flow = Slic3r::support_material_1st_layer_flow(&object, float(slicing_params.first_print_layer_height));
this->support_material_flow = Slic3r::support_material_flow(&object, float(slicing_params.layer_height));
this->support_material_interface_flow = Slic3r::support_material_interface_flow(&object, float(slicing_params.layer_height));
this->raft_interface_flow = support_material_interface_flow;
// Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um.
this->support_layer_height_min = scaled<coord_t>(0.01);
for (auto lh : print_config.min_layer_height.values)
this->support_layer_height_min = std::min(this->support_layer_height_min, std::max(0.01, lh));
for (auto layer : object.layers())
this->support_layer_height_min = std::min(this->support_layer_height_min, std::max(0.01, layer->height));
if (object_config.support_material_interface_layers.value == 0) {
// No interface layers allowed, print everything with the base support pattern.
this->support_material_interface_flow = this->support_material_flow;
}
// Evaluate the XY gap between the object outer perimeters and the support structures.
// Evaluate the XY gap between the object outer perimeters and the support structures.
coordf_t external_perimeter_width = 0.;
coordf_t bridge_flow_ratio = 0;
for (size_t region_id = 0; region_id < object.num_printing_regions(); ++ region_id) {
const PrintRegion &region = object.printing_region(region_id);
external_perimeter_width = std::max(external_perimeter_width, coordf_t(region.flow(object, frExternalPerimeter, slicing_params.layer_height).width()));
bridge_flow_ratio += region.config().bridge_flow_ratio;
}
this->gap_xy = object_config.support_material_xy_spacing.get_abs_value(external_perimeter_width);
bridge_flow_ratio /= object.num_printing_regions();
this->support_material_bottom_interface_flow = slicing_params.soluble_interface || ! object_config.thick_bridges ?
this->support_material_interface_flow.with_flow_ratio(bridge_flow_ratio) :
Flow::bridging_flow(bridge_flow_ratio * this->support_material_interface_flow.nozzle_diameter(), this->support_material_interface_flow.nozzle_diameter());
this->can_merge_support_regions = object_config.support_material_extruder.value == object_config.support_material_interface_extruder.value;
if (!this->can_merge_support_regions && (object_config.support_material_extruder.value == 0 || object_config.support_material_interface_extruder.value == 0)) {
// One of the support extruders is of "don't care" type.
auto object_extruders = object.object_extruders();
if (object_extruders.size() == 1 &&
*object_extruders.begin() == std::max<unsigned int>(object_config.support_material_extruder.value, object_config.support_material_interface_extruder.value))
// Object is printed with the same extruder as the support.
this->can_merge_support_regions = true;
}
double interface_spacing = object_config.support_material_interface_spacing.value + this->support_material_interface_flow.spacing();
this->interface_density = std::min(1., this->support_material_interface_flow.spacing() / interface_spacing);
double raft_interface_spacing = object_config.support_material_interface_spacing.value + this->raft_interface_flow.spacing();
this->raft_interface_density = std::min(1., this->raft_interface_flow.spacing() / raft_interface_spacing);
double support_spacing = object_config.support_material_spacing.value + this->support_material_flow.spacing();
this->support_density = std::min(1., this->support_material_flow.spacing() / support_spacing);
if (object_config.support_material_interface_layers.value == 0) {
// No interface layers allowed, print everything with the base support pattern.
this->interface_density = this->support_density;
}
SupportMaterialPattern support_pattern = object_config.support_material_pattern;
this->with_sheath = object_config.support_material_with_sheath;
this->base_fill_pattern =
support_pattern == smpHoneycomb ? ipHoneycomb :
this->support_density > 0.95 || this->with_sheath ? ipRectilinear : ipSupportBase;
this->interface_fill_pattern = (this->interface_density > 0.95 ? ipRectilinear : ipSupportBase);
this->raft_interface_fill_pattern = this->raft_interface_density > 0.95 ? ipRectilinear : ipSupportBase;
this->contact_fill_pattern =
(object_config.support_material_interface_pattern == smipAuto && slicing_params.soluble_interface) ||
object_config.support_material_interface_pattern == smipConcentric ?
ipConcentric :
(this->interface_density > 0.95 ? ipRectilinear : ipSupportBase);
this->base_angle = Geometry::deg2rad(float(object_config.support_material_angle.value));
this->interface_angle = Geometry::deg2rad(float(object_config.support_material_angle.value + 90.));
this->raft_angle_1st_layer = 0.f;
this->raft_angle_base = 0.f;
this->raft_angle_interface = 0.f;
if (slicing_params.base_raft_layers > 1) {
assert(slicing_params.raft_layers() >= 4);
// There are all raft layer types (1st layer, base, interface & contact layers) available.
this->raft_angle_1st_layer = this->interface_angle;
this->raft_angle_base = this->base_angle;
this->raft_angle_interface = this->interface_angle;
if ((slicing_params.interface_raft_layers & 1) == 0)
// Allign the 1st raft interface layer so that the object 1st layer is hatched perpendicularly to the raft contact interface.
this->raft_angle_interface += float(0.5 * M_PI);
} else if (slicing_params.base_raft_layers == 1 || slicing_params.interface_raft_layers > 1) {
assert(slicing_params.raft_layers() == 2 || slicing_params.raft_layers() == 3);
// 1st layer, interface & contact layers available.
this->raft_angle_1st_layer = this->base_angle;
this->raft_angle_interface = this->interface_angle + 0.5 * M_PI;
} else if (slicing_params.interface_raft_layers == 1) {
// Only the contact raft layer is non-empty, which will be printed as the 1st layer.
assert(slicing_params.base_raft_layers == 0);
assert(slicing_params.interface_raft_layers == 1);
assert(slicing_params.raft_layers() == 1);
this->raft_angle_1st_layer = float(0.5 * M_PI);
this->raft_angle_interface = this->raft_angle_1st_layer;
} else {
// No raft.
assert(slicing_params.base_raft_layers == 0);
assert(slicing_params.interface_raft_layers == 0);
assert(slicing_params.raft_layers() == 0);
}
this->tree_branch_diameter_double_wall_area_scaled = 0.25 * sqr(scaled<double>(object_config.support_tree_branch_diameter_double_wall.value)) * M_PI;
}
} // namespace Slic3r

View File

@ -0,0 +1,96 @@
#ifndef slic3r_SupportParameters_hpp_
#define slic3r_SupportParameters_hpp_
#include "../libslic3r.h"
#include "../Flow.hpp"
namespace Slic3r {
class PrintObject;
enum InfillPattern : int;
namespace FFFSupport {
struct SupportParameters {
SupportParameters(const PrintObject &object);
// Both top / bottom contacts and interfaces are soluble.
bool soluble_interface;
// Support contact & interface are soluble, but support base is non-soluble.
bool soluble_interface_non_soluble_base;
// Is there at least a top contact layer extruded above support base?
bool has_top_contacts;
// Is there at least a bottom contact layer extruded below support base?
bool has_bottom_contacts;
// Number of top interface layers without counting the contact layer.
size_t num_top_interface_layers;
// Number of bottom interface layers without counting the contact layer.
size_t num_bottom_interface_layers;
// Number of top base interface layers. Zero if not soluble_interface_non_soluble_base.
size_t num_top_base_interface_layers;
// Number of bottom base interface layers. Zero if not soluble_interface_non_soluble_base.
size_t num_bottom_base_interface_layers;
bool has_contacts() const { return this->has_top_contacts || this->has_bottom_contacts; }
bool has_interfaces() const { return this->num_top_interface_layers + this->num_bottom_interface_layers > 0; }
bool has_base_interfaces() const { return this->num_top_base_interface_layers + this->num_bottom_base_interface_layers > 0; }
size_t num_top_interface_layers_only() const { return this->num_top_interface_layers - this->num_top_base_interface_layers; }
size_t num_bottom_interface_layers_only() const { return this->num_bottom_interface_layers - this->num_bottom_base_interface_layers; }
// Flow at the 1st print layer.
Flow first_layer_flow;
// Flow at the support base (neither top, nor bottom interface).
// Also flow at the raft base with the exception of raft interface and contact layers.
Flow support_material_flow;
// Flow at the top interface and contact layers.
Flow support_material_interface_flow;
// Flow at the bottom interfaces and contacts.
Flow support_material_bottom_interface_flow;
// Flow at raft inteface & contact layers.
Flow raft_interface_flow;
// Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder?
bool can_merge_support_regions;
coordf_t support_layer_height_min;
// coordf_t support_layer_height_max;
coordf_t gap_xy;
float base_angle;
float interface_angle;
// Density of the top / bottom interface and contact layers.
coordf_t interface_density;
// Density of the raft interface and contact layers.
coordf_t raft_interface_density;
// Density of the base support layers.
coordf_t support_density;
// Pattern of the sparse infill including sparse raft layers.
InfillPattern base_fill_pattern;
// Pattern of the top / bottom interface and contact layers.
InfillPattern interface_fill_pattern;
// Pattern of the raft interface and contact layers.
InfillPattern raft_interface_fill_pattern;
// Pattern of the contact layers.
InfillPattern contact_fill_pattern;
// Shall the sparse (base) layers be printed with a single perimeter line (sheath) for robustness?
bool with_sheath;
// Branches of organic supports with area larger than this threshold will be extruded with double lines.
double tree_branch_diameter_double_wall_area_scaled;
float raft_angle_1st_layer;
float raft_angle_base;
float raft_angle_interface;
// Produce a raft interface angle for a given SupportLayer::interface_id()
float raft_interface_angle(size_t interface_id) const
{ return this->raft_angle_interface + ((interface_id & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.)); }
};
} // namespace FFFSupport
} // namespace Slic3r
#endif /* slic3r_SupportParameters_hpp_ */

View File

@ -9,14 +9,15 @@
#include "TreeModelVolumes.hpp"
#include "TreeSupport.hpp"
#include "BuildVolume.hpp"
#include "ClipperUtils.hpp"
#include "Flow.hpp"
#include "Layer.hpp"
#include "Point.hpp"
#include "Print.hpp"
#include "PrintConfig.hpp"
#include "Utils.hpp"
#include "../BuildVolume.hpp"
#include "../ClipperUtils.hpp"
#include "../Flow.hpp"
#include "../Layer.hpp"
#include "../Point.hpp"
#include "../Print.hpp"
#include "../PrintConfig.hpp"
#include "../Utils.hpp"
#include "../format.hpp"
#include <string_view>
@ -34,6 +35,8 @@ using namespace std::literals;
// had to use a define beacuse the macro processing inside macro BOOST_LOG_TRIVIAL()
#define error_level_not_in_cache error
static constexpr const bool polygons_strictly_simple = false;
TreeSupportMeshGroupSettings::TreeSupportMeshGroupSettings(const PrintObject &print_object)
{
const PrintConfig &print_config = print_object.print()->config();
@ -76,7 +79,9 @@ TreeSupportMeshGroupSettings::TreeSupportMeshGroupSettings(const PrintObject &pr
// this->support_interface_skip_height =
// this->support_infill_angles =
this->support_roof_enable = config.support_material_interface_layers.value > 0;
this->support_roof_height = config.support_material_interface_layers.value * this->layer_height;
this->support_roof_layers = this->support_roof_enable ? config.support_material_interface_layers.value : 0;
this->support_floor_enable = config.support_material_interface_layers.value > 0 && config.support_material_bottom_interface_layers.value > 0;
this->support_floor_layers = this->support_floor_enable ? config.support_material_bottom_interface_layers.value : 0;
// this->minimum_roof_area =
// this->support_roof_angles =
this->support_roof_pattern = config.support_material_interface_pattern;
@ -175,7 +180,7 @@ TreeModelVolumes::TreeModelVolumes(
tbb::parallel_for(tbb::blocked_range<size_t>(num_raft_layers, num_layers, std::min<size_t>(1, std::max<size_t>(16, num_layers / (8 * tbb::this_task_arena::max_concurrency())))),
[&](const tbb::blocked_range<size_t> &range) {
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx)
outlines[layer_idx] = to_polygons(expolygons_simplify(print_object.get_layer(layer_idx - num_raft_layers)->lslices, mesh_settings.resolution));
outlines[layer_idx] = polygons_simplify(to_polygons(print_object.get_layer(layer_idx - num_raft_layers)->lslices), mesh_settings.resolution, polygons_strictly_simple);
});
}
#endif
@ -257,6 +262,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));
@ -422,7 +429,7 @@ const Polygons& TreeModelVolumes::getPlaceableAreas(const coord_t orig_radius, L
return (*result).get();
if (m_precalculated) {
BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate Placeable Areas at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!";
tree_supports_show_error("Not precalculated Placeable areas requested."sv, false);
tree_supports_show_error(format("Not precalculated Placeable areas requested, radius %1%, layer %2%", radius, layer_idx), false);
}
if (orig_radius == 0)
// Placable areas for radius 0 are calculated in the general collision code.
@ -583,7 +590,7 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex
if (processing_last_mesh) {
if (! dst.empty())
collisions = union_(collisions, dst);
dst = polygons_simplify(collisions, min_resolution);
dst = polygons_simplify(collisions, min_resolution, polygons_strictly_simple);
} else
append(dst, std::move(collisions));
throw_on_cancel();
@ -593,21 +600,24 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex
// 3) Optionally calculate placables.
if (calculate_placable) {
// Now calculate the placable areas.
tbb::parallel_for(tbb::blocked_range<LayerIndex>(std::max(data.idx_begin, 1), data.idx_end),
[&collision_areas_offsetted, &anti_overhang = m_anti_overhang, processing_last_mesh,
min_resolution = m_min_resolution, &data_placeable, &throw_on_cancel]
tbb::parallel_for(tbb::blocked_range<LayerIndex>(std::max(z_distance_bottom_layers + 1, data.idx_begin), data.idx_end),
[&collision_areas_offsetted, &outlines, &anti_overhang = m_anti_overhang, processing_last_mesh,
min_resolution = m_min_resolution, z_distance_bottom_layers, xy_distance, &data_placeable, &throw_on_cancel]
(const tbb::blocked_range<LayerIndex>& range) {
for (LayerIndex layer_idx = range.begin(); layer_idx != range.end(); ++ layer_idx) {
LayerIndex layer_idx_below = layer_idx - 1;
LayerIndex layer_idx_below = layer_idx - z_distance_bottom_layers - 1;
assert(layer_idx_below >= 0);
const Polygons &current = collision_areas_offsetted[layer_idx];
const Polygons &below = collision_areas_offsetted[layer_idx_below];
Polygons placable = diff(below, layer_idx_below < int(anti_overhang.size()) ? union_(current, anti_overhang[layer_idx_below]) : current);
const Polygons &below = outlines[layer_idx_below];
Polygons placable = diff(
// Inflate the surface to sit on by the separation distance to increase chance of a support being placed on a sloped surface.
offset(below, xy_distance),
layer_idx_below < int(anti_overhang.size()) ? union_(current, anti_overhang[layer_idx_below]) : current);
auto &dst = data_placeable[layer_idx];
if (processing_last_mesh) {
if (! dst.empty())
placable = union_(placable, dst);
dst = polygons_simplify(placable, min_resolution);
dst = polygons_simplify(placable, min_resolution, polygons_strictly_simple);
} else
append(dst, placable);
throw_on_cancel();
@ -655,7 +665,7 @@ void TreeModelVolumes::calculateCollisionHolefree(const std::vector<RadiusLayerP
data.emplace_back(RadiusLayerPair(radius, layer_idx), polygons_simplify(
offset(union_ex(this->getCollision(m_increase_until_radius, layer_idx, false)),
5 - increase_radius_ceil, ClipperLib::jtRound, m_min_resolution),
m_min_resolution));
m_min_resolution, polygons_strictly_simple));
throw_on_cancel();
}
}
@ -742,7 +752,7 @@ void TreeModelVolumes::calculateAvoidance(const std::vector<RadiusLayerPair> &ke
ClipperLib::jtRound, m_min_resolution));
if (task.to_model)
latest_avoidance = diff(latest_avoidance, getPlaceableAreas(task.radius, layer_idx, throw_on_cancel));
latest_avoidance = polygons_simplify(latest_avoidance, m_min_resolution);
latest_avoidance = polygons_simplify(latest_avoidance, m_min_resolution, polygons_strictly_simple);
data.emplace_back(RadiusLayerPair{task.radius, layer_idx}, latest_avoidance);
throw_on_cancel();
}
@ -863,12 +873,12 @@ void TreeModelVolumes::calculateWallRestrictions(const std::vector<RadiusLayerPa
data[layer_idx - min_layer_bottom] = polygons_simplify(
// radius contains m_current_min_xy_dist_delta already if required
intersection(getCollision(0, layer_idx, false), getCollision(radius, layer_idx - 1, true)),
m_min_resolution);
m_min_resolution, polygons_strictly_simple);
if (! data_min.empty())
data_min[layer_idx - min_layer_bottom] =
polygons_simplify(
intersection(getCollision(0, layer_idx, true), getCollision(radius, layer_idx - 1, true)),
m_min_resolution);
m_min_resolution, polygons_strictly_simple);
throw_on_cancel();
}
});

View File

@ -14,9 +14,9 @@
#include <boost/functional/hash.hpp>
#include "Point.hpp"
#include "Polygon.hpp"
#include "PrintConfig.hpp"
#include "../Point.hpp"
#include "../Polygon.hpp"
#include "../PrintConfig.hpp"
namespace Slic3r
{
@ -94,7 +94,9 @@ struct TreeSupportMeshGroupSettings {
bool support_roof_enable { false };
// Support Roof Thickness
// The thickness of the support roofs. This controls the amount of dense layers at the top of the support on which the model rests.
coord_t support_roof_height { scaled<coord_t>(1.) };
coord_t support_roof_layers { 2 };
bool support_floor_enable { false };
coord_t support_floor_layers { 2 };
// Minimum Support Roof Area
// Minimum area size for the roofs of the support. Polygons which have an area smaller than this value will be printed as normal support.
double minimum_roof_area { scaled<double>(scaled<double>(1.)) };
@ -215,6 +217,7 @@ public:
void clear() {
this->clear_all_but_object_collision();
m_collision_cache.clear();
m_placeable_areas_cache.clear();
}
void clear_all_but_object_collision() {
//m_collision_cache.clear_all_but_radius0();
@ -223,7 +226,7 @@ public:
m_avoidance_cache_slow.clear();
m_avoidance_cache_to_model.clear();
m_avoidance_cache_to_model_slow.clear();
m_placeable_areas_cache.clear();
m_placeable_areas_cache.clear_all_but_radius0();
m_avoidance_cache_holefree.clear();
m_avoidance_cache_holefree_to_model.clear();
m_wall_restrictions_cache.clear();

View File

@ -11,6 +11,7 @@
#include "TreeModelVolumes.hpp"
#include "Point.hpp"
#include "Support/SupportLayer.hpp"
#include <boost/container/small_vector.hpp>
@ -39,10 +40,7 @@ namespace Slic3r
// Forward declarations
class Print;
class PrintObject;
class SupportGeneratorLayer;
struct SlicingParameters;
using SupportGeneratorLayerStorage = std::deque<SupportGeneratorLayer>;
using SupportGeneratorLayersPtr = std::vector<SupportGeneratorLayer*>;
namespace FFFTreeSupport
{
@ -93,6 +91,8 @@ struct AreaIncreaseSettings
struct TreeSupportSettings;
// #define TREE_SUPPORTS_TRACK_LOST
// C++17 does not support in place initializers of bit values, thus a constructor zeroing the bits is provided.
struct SupportElementStateBits {
SupportElementStateBits() :
@ -102,6 +102,10 @@ struct SupportElementStateBits {
supports_roof(false),
can_use_safe_radius(false),
skip_ovalisation(false),
#ifdef TREE_SUPPORTS_TRACK_LOST
lost(false),
verylost(false),
#endif // TREE_SUPPORTS_TRACK_LOST
deleted(false),
marked(false)
{}
@ -136,6 +140,12 @@ struct SupportElementStateBits {
*/
bool skip_ovalisation : 1;
#ifdef TREE_SUPPORTS_TRACK_LOST
// Likely a lost branch, debugging information.
bool lost : 1;
bool verylost : 1;
#endif // TREE_SUPPORTS_TRACK_LOST
// Not valid anymore, to be deleted.
bool deleted : 1;
@ -302,9 +312,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 +340,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-
*/
@ -353,10 +364,6 @@ public:
* \brief Amount of layers distance required from the top of the model to the bottom of a support structure.
*/
size_t z_distance_bottom_layers;
/*!
* \brief used for performance optimization at the support floor. Should have no impact on the resulting tree.
*/
size_t performance_interface_skip_layers;
/*!
* \brief User specified angles for the support infill.
*/
@ -418,7 +425,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 +479,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 +511,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;
}
/*!

View File

@ -1,314 +0,0 @@
#ifndef slic3r_SupportMaterial_hpp_
#define slic3r_SupportMaterial_hpp_
#include "Flow.hpp"
#include "PrintConfig.hpp"
#include "Slicing.hpp"
namespace Slic3r {
class PrintObject;
class PrintConfig;
class PrintObjectConfig;
// Support layer type to be used by SupportGeneratorLayer. This type carries a much more detailed information
// about the support layer type than the final support layers stored in a PrintObject.
enum class SupporLayerType {
Unknown = 0,
// Ratft base layer, to be printed with the support material.
RaftBase,
// Raft interface layer, to be printed with the support interface material.
RaftInterface,
// Bottom contact layer placed over a top surface of an object. To be printed with a support interface material.
BottomContact,
// Dense interface layer, to be printed with the support interface material.
// This layer is separated from an object by an BottomContact layer.
BottomInterface,
// Sparse base support layer, to be printed with a support material.
Base,
// Dense interface layer, to be printed with the support interface material.
// This layer is separated from an object with TopContact layer.
TopInterface,
// Top contact layer directly supporting an overhang. To be printed with a support interface material.
TopContact,
// Some undecided type yet. It will turn into Base first, then it may turn into BottomInterface or TopInterface.
Intermediate,
};
// A support layer type used internally by the SupportMaterial class. This class carries a much more detailed
// information about the support layer than the layers stored in the PrintObject, mainly
// the SupportGeneratorLayer is aware of the bridging flow and the interface gaps between the object and the support.
class SupportGeneratorLayer
{
public:
void reset() {
*this = SupportGeneratorLayer();
}
bool operator==(const SupportGeneratorLayer &layer2) const {
return print_z == layer2.print_z && height == layer2.height && bridging == layer2.bridging;
}
// Order the layers by lexicographically by an increasing print_z and a decreasing layer height.
bool operator<(const SupportGeneratorLayer &layer2) const {
if (print_z < layer2.print_z) {
return true;
} else if (print_z == layer2.print_z) {
if (height > layer2.height)
return true;
else if (height == layer2.height) {
// Bridging layers first.
return bridging && ! layer2.bridging;
} else
return false;
} else
return false;
}
void merge(SupportGeneratorLayer &&rhs) {
// The union_() does not support move semantic yet, but maybe one day it will.
this->polygons = union_(this->polygons, std::move(rhs.polygons));
auto merge = [](std::unique_ptr<Polygons> &dst, std::unique_ptr<Polygons> &src) {
if (! dst || dst->empty())
dst = std::move(src);
else if (src && ! src->empty())
*dst = union_(*dst, std::move(*src));
};
merge(this->contact_polygons, rhs.contact_polygons);
merge(this->overhang_polygons, rhs.overhang_polygons);
merge(this->enforcer_polygons, rhs.enforcer_polygons);
rhs.reset();
}
// For the bridging flow, bottom_print_z will be above bottom_z to account for the vertical separation.
// For the non-bridging flow, bottom_print_z will be equal to bottom_z.
coordf_t bottom_print_z() const { return print_z - height; }
// To sort the extremes of top / bottom interface layers.
coordf_t extreme_z() const { return (this->layer_type == SupporLayerType::TopContact) ? this->bottom_z : this->print_z; }
SupporLayerType layer_type { SupporLayerType::Unknown };
// Z used for printing, in unscaled coordinates.
coordf_t print_z { 0 };
// Bottom Z of this layer. For soluble layers, bottom_z + height = print_z,
// otherwise bottom_z + gap + height = print_z.
coordf_t bottom_z { 0 };
// Layer height in unscaled coordinates.
coordf_t height { 0 };
// Index of a PrintObject layer_id supported by this layer. This will be set for top contact layers.
// If this is not a contact layer, it will be set to size_t(-1).
size_t idx_object_layer_above { size_t(-1) };
// Index of a PrintObject layer_id, which supports this layer. This will be set for bottom contact layers.
// If this is not a contact layer, it will be set to size_t(-1).
size_t idx_object_layer_below { size_t(-1) };
// Use a bridging flow when printing this support layer.
bool bridging { false };
// Polygons to be filled by the support pattern.
Polygons polygons;
// Currently for the contact layers only.
std::unique_ptr<Polygons> contact_polygons;
std::unique_ptr<Polygons> overhang_polygons;
// Enforcers need to be propagated independently in case the "support on build plate only" option is enabled.
std::unique_ptr<Polygons> enforcer_polygons;
};
// Layers are allocated and owned by a deque. Once a layer is allocated, it is maintained
// up to the end of a generate() method. The layer storage may be replaced by an allocator class in the future,
// which would allocate layers by multiple chunks.
using SupportGeneratorLayerStorage = std::deque<SupportGeneratorLayer>;
using SupportGeneratorLayersPtr = std::vector<SupportGeneratorLayer*>;
struct SupportParameters {
SupportParameters(const PrintObject &object);
// Flow at the 1st print layer.
Flow first_layer_flow;
// Flow at the support base (neither top, nor bottom interface).
// Also flow at the raft base with the exception of raft interface and contact layers.
Flow support_material_flow;
// Flow at the top interface and contact layers.
Flow support_material_interface_flow;
// Flow at the bottom interfaces and contacts.
Flow support_material_bottom_interface_flow;
// Flow at raft inteface & contact layers.
Flow raft_interface_flow;
// Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder?
bool can_merge_support_regions;
coordf_t support_layer_height_min;
// coordf_t support_layer_height_max;
coordf_t gap_xy;
float base_angle;
float interface_angle;
// Density of the top / bottom interface and contact layers.
coordf_t interface_density;
// Density of the raft interface and contact layers.
coordf_t raft_interface_density;
// Density of the base support layers.
coordf_t support_density;
// Pattern of the sparse infill including sparse raft layers.
InfillPattern base_fill_pattern;
// Pattern of the top / bottom interface and contact layers.
InfillPattern interface_fill_pattern;
// Pattern of the raft interface and contact layers.
InfillPattern raft_interface_fill_pattern;
// Pattern of the contact layers.
InfillPattern contact_fill_pattern;
// Shall the sparse (base) layers be printed with a single perimeter line (sheath) for robustness?
bool with_sheath;
float raft_angle_1st_layer;
float raft_angle_base;
float raft_angle_interface;
// Produce a raft interface angle for a given SupportLayer::interface_id()
float raft_interface_angle(size_t interface_id) const
{ return this->raft_angle_interface + ((interface_id & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.)); }
};
// Remove bridges from support contact areas.
// To be called if PrintObjectConfig::dont_support_bridges.
void remove_bridges_from_contacts(
const PrintConfig &print_config,
const Layer &lower_layer,
const LayerRegion &layerm,
float fw,
Polygons &contact_polygons);
// Generate raft layers, also expand the 1st support layer
// in case there is no raft layer to improve support adhesion.
SupportGeneratorLayersPtr generate_raft_base(
const PrintObject &object,
const SupportParameters &support_params,
const SlicingParameters &slicing_params,
const SupportGeneratorLayersPtr &top_contacts,
const SupportGeneratorLayersPtr &interface_layers,
const SupportGeneratorLayersPtr &base_interface_layers,
const SupportGeneratorLayersPtr &base_layers,
SupportGeneratorLayerStorage &layer_storage);
// returns sorted layers
SupportGeneratorLayersPtr generate_support_layers(
PrintObject &object,
const SupportGeneratorLayersPtr &raft_layers,
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
const SupportGeneratorLayersPtr &intermediate_layers,
const SupportGeneratorLayersPtr &interface_layers,
const SupportGeneratorLayersPtr &base_interface_layers);
// Produce the support G-code.
// Used by both classic and tree supports.
void generate_support_toolpaths(
SupportLayerPtrs &support_layers,
const PrintObjectConfig &config,
const SupportParameters &support_params,
const SlicingParameters &slicing_params,
const SupportGeneratorLayersPtr &raft_layers,
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
const SupportGeneratorLayersPtr &intermediate_layers,
const SupportGeneratorLayersPtr &interface_layers,
const SupportGeneratorLayersPtr &base_interface_layers);
void export_print_z_polygons_to_svg(const char *path, SupportGeneratorLayer ** const layers, size_t n_layers);
void export_print_z_polygons_and_extrusions_to_svg(const char *path, SupportGeneratorLayer ** const layers, size_t n_layers, SupportLayer& support_layer);
// This class manages raft and supports for a single PrintObject.
// Instantiated by Slic3r::Print::Object->_support_material()
// This class is instantiated before the slicing starts as Object.pm will query
// the parameters of the raft to determine the 1st layer height and thickness.
class PrintObjectSupportMaterial
{
public:
PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params);
// Is raft enabled?
bool has_raft() const { return m_slicing_params.has_raft(); }
// Has any support?
bool has_support() const { return m_object_config->support_material.value || m_object_config->support_material_enforce_layers; }
bool build_plate_only() const { return this->has_support() && m_object_config->support_material_buildplate_only.value; }
bool synchronize_layers() const { return m_slicing_params.soluble_interface && m_object_config->support_material_synchronize_layers.value; }
bool has_contact_loops() const { return m_object_config->support_material_interface_contact_loops.value; }
// Generate support material for the object.
// New support layers will be added to the object,
// with extrusion paths and islands filled in for each support layer.
void generate(PrintObject &object);
private:
std::vector<Polygons> buildplate_covered(const PrintObject &object) const;
// Generate top contact layers supporting overhangs.
// For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined.
// If supports over bed surface only are requested, don't generate contact layers over an object.
SupportGeneratorLayersPtr top_contact_layers(const PrintObject &object, const std::vector<Polygons> &buildplate_covered, SupportGeneratorLayerStorage &layer_storage) const;
// Generate bottom contact layers supporting the top contact layers.
// For a soluble interface material synchronize the layer heights with the object,
// otherwise set the layer height to a bridging flow of a support interface nozzle.
SupportGeneratorLayersPtr bottom_contact_layers_and_layer_support_areas(
const PrintObject &object, const SupportGeneratorLayersPtr &top_contacts, std::vector<Polygons> &buildplate_covered,
SupportGeneratorLayerStorage &layer_storage, std::vector<Polygons> &layer_support_areas) const;
// Trim the top_contacts layers with the bottom_contacts layers if they overlap, so there would not be enough vertical space for both of them.
void trim_top_contacts_by_bottom_contacts(const PrintObject &object, const SupportGeneratorLayersPtr &bottom_contacts, SupportGeneratorLayersPtr &top_contacts) const;
// Generate raft layers and the intermediate support layers between the bottom contact and top contact surfaces.
SupportGeneratorLayersPtr raft_and_intermediate_support_layers(
const PrintObject &object,
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
SupportGeneratorLayerStorage &layer_storage) const;
// Fill in the base layers with polygons.
void generate_base_layers(
const PrintObject &object,
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
SupportGeneratorLayersPtr &intermediate_layers,
const std::vector<Polygons> &layer_support_areas) const;
// Turn some of the base layers into base interface layers.
// For soluble interfaces with non-soluble bases, print maximum two first interface layers with the base
// extruder to improve adhesion of the soluble filament to the base.
std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interface_layers(
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
SupportGeneratorLayersPtr &intermediate_layers,
SupportGeneratorLayerStorage &layer_storage) const;
// Trim support layers by an object to leave a defined gap between
// the support volume and the object.
void trim_support_layers_by_object(
const PrintObject &object,
SupportGeneratorLayersPtr &support_layers,
const coordf_t gap_extra_above,
const coordf_t gap_extra_below,
const coordf_t gap_xy) const;
/*
void generate_pillars_shape();
void clip_with_shape();
*/
// Following objects are not owned by SupportMaterial class.
const PrintConfig *m_print_config;
const PrintObjectConfig *m_object_config;
// Pre-calculated parameters shared between the object slicer and the support generator,
// carrying information on a raft, 1st layer height, 1st object layer height, gap between the raft and object etc.
SlicingParameters m_slicing_params;
// Various precomputed support parameters to be shared with external functions.
SupportParameters m_support_params;
};
} // namespace Slic3r
#endif /* slic3r_SupportMaterial_hpp_ */

View File

@ -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,10 @@
#include <cstddef>
#include <cstdio>
#include <functional>
#include <limits>
#include <math.h>
#include <oneapi/tbb/concurrent_vector.h>
#include <oneapi/tbb/parallel_for.h>
#include <optional>
#include <unordered_map>
#include <unordered_set>
@ -169,6 +174,69 @@ struct SliceConnection
}
};
SliceConnection estimate_slice_connection(size_t slice_idx, const Layer *layer)
{
SliceConnection connection;
const LayerSlice &slice = layer->lslices_ex[slice_idx];
Polygons slice_polys = to_polygons(layer->lslices[slice_idx]);
BoundingBox slice_bb = get_extents(slice_polys);
const Layer *lower_layer = layer->lower_layer;
ExPolygons below{};
for (const auto &link : slice.overlaps_below) { below.push_back(lower_layer->lslices[link.slice_idx]); }
Polygons below_polys = to_polygons(below);
BoundingBox below_bb = get_extents(below_polys);
Polygons overlap = intersection(ClipperUtils::clip_clipper_polygons_with_subject_bbox(slice_polys, below_bb),
ClipperUtils::clip_clipper_polygons_with_subject_bbox(below_polys, slice_bb));
for (const Polygon &poly : overlap) {
Vec2f p0 = unscaled(poly.first_point()).cast<float>();
for (size_t i = 2; i < poly.points.size(); i++) {
Vec2f p1 = unscaled(poly.points[i - 1]).cast<float>();
Vec2f p2 = unscaled(poly.points[i]).cast<float>();
float sign = cross2(p1 - p0, p2 - p1) > 0 ? 1.0f : -1.0f;
auto [area, first_moment_of_area, second_moment_area,
second_moment_of_area_covariance] = compute_moments_of_area_of_triangle(p0, p1, p2);
connection.area += sign * area;
connection.centroid_accumulator += sign * Vec3f(first_moment_of_area.x(), first_moment_of_area.y(), layer->print_z * area);
connection.second_moment_of_area_accumulator += sign * second_moment_area;
connection.second_moment_of_area_covariance_accumulator += sign * second_moment_of_area_covariance;
}
}
return connection;
};
using PrecomputedSliceConnections = std::vector<std::vector<SliceConnection>>;
PrecomputedSliceConnections precompute_slices_connections(const PrintObject *po)
{
PrecomputedSliceConnections result{};
for (size_t lidx = 0; lidx < po->layer_count(); lidx++) {
result.emplace_back(std::vector<SliceConnection>{});
for (size_t slice_idx = 0; slice_idx < po->get_layer(lidx)->lslices_ex.size(); slice_idx++) {
result[lidx].push_back(SliceConnection{});
}
}
tbb::parallel_for(tbb::blocked_range<size_t>(0, po->layers().size()), [po, &result](tbb::blocked_range<size_t> r) {
for (size_t lidx = r.begin(); lidx < r.end(); lidx++) {
const Layer *l = po->get_layer(lidx);
tbb::parallel_for(tbb::blocked_range<size_t>(0, l->lslices_ex.size()), [lidx, l, &result](tbb::blocked_range<size_t> r2) {
for (size_t slice_idx = r2.begin(); slice_idx < r2.end(); slice_idx++) {
result[lidx][slice_idx] = estimate_slice_connection(slice_idx, l);
}
});
}
});
return result;
};
float get_flow_width(const LayerRegion *region, ExtrusionRole role)
{
if (role == ExtrusionRole::BridgeInfill) return region->flow(FlowRole::frExternalPerimeter).width();
@ -206,18 +274,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);
}
@ -230,15 +318,8 @@ std::vector<ExtrusionLine> check_extrusion_entity_stability(const ExtrusionEntit
const AABBTreeLines::LinesDistancer<Linef> &prev_layer_boundary,
const Params &params)
{
if (entity->is_collection()) {
std::vector<ExtrusionLine> checked_lines_out;
checked_lines_out.reserve(prev_layer_lines.get_lines().size() / 3);
for (const auto *e : static_cast<const ExtrusionEntityCollection *>(entity)->entities) {
auto tmp = check_extrusion_entity_stability(e, layer_region, prev_layer_lines, prev_layer_boundary, params);
checked_lines_out.insert(checked_lines_out.end(), tmp.begin(), tmp.end());
}
return checked_lines_out;
} else if (entity->role().is_bridge() && !entity->role().is_perimeter()) {
assert(!entity->is_collection());
if (entity->role().is_bridge() && !entity->role().is_perimeter()) {
// pure bridges are handled separately, beacuse we need to align the forward and backward direction support points
if (entity->length() < scale_(params.min_distance_to_allow_local_supports)) {
return {};
@ -312,18 +393,19 @@ 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;
curr_point.distance *= sign;
SupportPointCause potential_cause = SupportPointCause::FloatingExtrusion;
if (bridged_distance + line_len > params.bridge_distance * 0.8 && std::abs(curr_point.curvature) < 0.1) {
potential_cause = SupportPointCause::FloatingExtrusion;
}
// Bridges are now separated. While long overhang perimeter is technically bridge, it would confuse the users
// if (bridged_distance + line_len > params.bridge_distance * 0.8 && std::abs(curr_point.curvature) < 0.1) {
// potential_cause = SupportPointCause::FloatingExtrusion;
// }
float max_bridge_len = std::max(params.support_points_interface_radius * 2.0f,
params.bridge_distance /
@ -339,7 +421,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 +431,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);
}
@ -359,44 +442,6 @@ std::vector<ExtrusionLine> check_extrusion_entity_stability(const ExtrusionEntit
}
}
SliceConnection estimate_slice_connection(size_t slice_idx, const Layer *layer)
{
SliceConnection connection;
const LayerSlice &slice = layer->lslices_ex[slice_idx];
Polygons slice_polys = to_polygons(layer->lslices[slice_idx]);
BoundingBox slice_bb = get_extents(slice_polys);
const Layer *lower_layer = layer->lower_layer;
ExPolygons below{};
for (const auto &link : slice.overlaps_below) { below.push_back(lower_layer->lslices[link.slice_idx]); }
Polygons below_polys = to_polygons(below);
BoundingBox below_bb = get_extents(below_polys);
Polygons overlap = intersection(ClipperUtils::clip_clipper_polygons_with_subject_bbox(slice_polys, below_bb),
ClipperUtils::clip_clipper_polygons_with_subject_bbox(below_polys, slice_bb));
for (const Polygon &poly : overlap) {
Vec2f p0 = unscaled(poly.first_point()).cast<float>();
for (size_t i = 2; i < poly.points.size(); i++) {
Vec2f p1 = unscaled(poly.points[i - 1]).cast<float>();
Vec2f p2 = unscaled(poly.points[i]).cast<float>();
float sign = cross2(p1 - p0, p2 - p1) > 0 ? 1.0f : -1.0f;
auto [area, first_moment_of_area, second_moment_area,
second_moment_of_area_covariance] = compute_moments_of_area_of_triangle(p0, p1, p2);
connection.area += sign * area;
connection.centroid_accumulator += sign * Vec3f(first_moment_of_area.x(), first_moment_of_area.y(), layer->print_z * area);
connection.second_moment_of_area_accumulator += sign * second_moment_area;
connection.second_moment_of_area_covariance_accumulator += sign * second_moment_of_area_covariance;
}
}
return connection;
};
class ObjectPart
{
public:
@ -737,7 +782,10 @@ public:
}
};
std::tuple<SupportPoints, PartialObjects> check_stability(const PrintObject *po, const PrintTryCancel &cancel_func, const Params &params)
std::tuple<SupportPoints, PartialObjects> check_stability(const PrintObject *po,
const PrecomputedSliceConnections &precomputed_slices_connections,
const PrintTryCancel &cancel_func,
const Params &params)
{
SupportPoints supp_points{};
SupportGridFilter supports_presence_grid(po, params.min_distance_between_support_points);
@ -766,8 +814,8 @@ std::tuple<SupportPoints, PartialObjects> check_stability(const PrintObject *po,
for (size_t slice_idx = 0; slice_idx < layer->lslices_ex.size(); ++slice_idx) {
const LayerSlice &slice = layer->lslices_ex.at(slice_idx);
auto [new_part, covered_area] = build_object_part_from_slice(slice_idx, layer, params);
SliceConnection connection_to_below = estimate_slice_connection(slice_idx, layer);
auto [new_part, covered_area] = build_object_part_from_slice(slice_idx, layer, params);
const SliceConnection &connection_to_below = precomputed_slices_connections[layer_idx][slice_idx];
#ifdef DETAILED_DEBUG_LOGS
std::cout << "SLICE IDX: " << slice_idx << std::endl;
@ -834,25 +882,87 @@ std::tuple<SupportPoints, PartialObjects> check_stability(const PrintObject *po,
prev_slice_idx_to_weakest_connection = next_slice_idx_to_weakest_connection;
next_slice_idx_to_weakest_connection.clear();
auto get_flat_entities = [](const ExtrusionEntity *e) {
std::vector<const ExtrusionEntity *> entities;
std::vector<const ExtrusionEntity *> queue{e};
while (!queue.empty()) {
const ExtrusionEntity *next = queue.back();
queue.pop_back();
if (next->is_collection()) {
for (const ExtrusionEntity *e : static_cast<const ExtrusionEntityCollection *>(next)->entities) {
queue.push_back(e);
}
} else {
entities.push_back(next);
}
}
return entities;
};
struct EnitityToCheck
{
const ExtrusionEntity *e;
const LayerRegion *region;
size_t slice_idx;
};
std::vector<EnitityToCheck> entities_to_check;
for (size_t slice_idx = 0; slice_idx < layer->lslices_ex.size(); ++slice_idx) {
const LayerSlice &slice = layer->lslices_ex.at(slice_idx);
for (const auto &island : slice.islands) {
for (const LayerExtrusionRange &fill_range : island.fills) {
const LayerRegion *fill_region = layer->get_region(fill_range.region());
for (const auto &fill_idx : fill_range) {
for (const ExtrusionEntity *e : get_flat_entities(fill_region->fills().entities[fill_idx])) {
if (e->role() == ExtrusionRole::BridgeInfill) {
entities_to_check.push_back({e, fill_region, slice_idx});
}
}
}
}
const LayerRegion *perimeter_region = layer->get_region(island.perimeters.region());
for (const size_t &perimeter_idx : island.perimeters) {
for (const ExtrusionEntity *e : get_flat_entities(perimeter_region->perimeters().entities[perimeter_idx])) {
entities_to_check.push_back({e, perimeter_region, slice_idx});
}
}
}
}
AABBTreeLines::LinesDistancer<Linef> prev_layer_boundary = layer->lower_layer != nullptr ?
AABBTreeLines::LinesDistancer<Linef>{
to_unscaled_linesf(layer->lower_layer->lslices)} :
AABBTreeLines::LinesDistancer<Linef>{};
std::vector<tbb::concurrent_vector<ExtrusionLine>> unstable_lines_per_slice(layer->lslices_ex.size());
std::vector<tbb::concurrent_vector<ExtrusionLine>> ext_perim_lines_per_slice(layer->lslices_ex.size());
tbb::parallel_for(tbb::blocked_range<size_t>(0, entities_to_check.size()),
[&entities_to_check, &prev_layer_ext_perim_lines, &prev_layer_boundary, &unstable_lines_per_slice,
&ext_perim_lines_per_slice, &params](tbb::blocked_range<size_t> r) {
for (size_t entity_idx = r.begin(); entity_idx < r.end(); ++entity_idx) {
const auto &e_to_check = entities_to_check[entity_idx];
for (const auto &line :
check_extrusion_entity_stability(e_to_check.e, e_to_check.region, prev_layer_ext_perim_lines,
prev_layer_boundary, params)) {
if (line.support_point_generated.has_value()) {
unstable_lines_per_slice[e_to_check.slice_idx].push_back(line);
}
if (line.is_external_perimeter()) {
ext_perim_lines_per_slice[e_to_check.slice_idx].push_back(line);
}
}
}
});
std::vector<ExtrusionLine> current_layer_ext_perims_lines{};
current_layer_ext_perims_lines.reserve(prev_layer_ext_perim_lines.get_lines().size());
// All object parts updated, and for each slice we have coresponding weakest connection.
// We can now check each slice and its corresponding weakest connection and object part for stability.
for (size_t slice_idx = 0; slice_idx < layer->lslices_ex.size(); ++slice_idx) {
const LayerSlice &slice = layer->lslices_ex.at(slice_idx);
ObjectPart &part = active_object_parts.access(prev_slice_idx_to_object_part_mapping[slice_idx]);
SliceConnection &weakest_conn = prev_slice_idx_to_weakest_connection[slice_idx];
std::vector<Linef> boundary_lines;
for (const auto &link : slice.overlaps_below) {
auto ls = to_unscaled_linesf({layer->lower_layer->lslices[link.slice_idx]});
boundary_lines.insert(boundary_lines.end(), ls.begin(), ls.end());
}
AABBTreeLines::LinesDistancer<Linef> prev_layer_boundary{std::move(boundary_lines)};
std::vector<ExtrusionLine> current_slice_ext_perims_lines{};
current_slice_ext_perims_lines.reserve(prev_layer_ext_perim_lines.get_lines().size() / layer->lslices_ex.size());
#ifdef DETAILED_DEBUG_LOGS
weakest_conn.print_info("weakest connection info: ");
#endif
@ -887,73 +997,15 @@ std::tuple<SupportPoints, PartialObjects> check_stability(const PrintObject *po,
}
};
// first we will check local extrusion stability of bridges, then of perimeters. Perimeters are more important, they
// account for most of the curling and possible crashes, so on them we will run also global stability check
for (const auto &island : slice.islands) {
// Support bridges where needed.
for (const LayerExtrusionRange &fill_range : island.fills) {
const LayerRegion *fill_region = layer->get_region(fill_range.region());
for (const auto &fill_idx : fill_range) {
const ExtrusionEntity *entity = fill_region->fills().entities[fill_idx];
if (entity->role() == ExtrusionRole::BridgeInfill) {
for (const ExtrusionLine &bridge :
check_extrusion_entity_stability(entity, fill_region, prev_layer_ext_perim_lines, prev_layer_boundary,
params)) {
if (bridge.support_point_generated.has_value()) {
reckon_new_support_point(*bridge.support_point_generated, create_support_point_position(bridge.b),
float(-EPSILON), Vec2f::Zero());
}
}
}
}
}
const LayerRegion *perimeter_region = layer->get_region(island.perimeters.region());
for (const auto &perimeter_idx : island.perimeters) {
const ExtrusionEntity *entity = perimeter_region->perimeters().entities[perimeter_idx];
std::vector<ExtrusionLine> perims = check_extrusion_entity_stability(entity, perimeter_region,
prev_layer_ext_perim_lines, prev_layer_boundary,
params);
for (const ExtrusionLine &perim : perims) {
if (perim.support_point_generated.has_value()) {
reckon_new_support_point(*perim.support_point_generated, create_support_point_position(perim.b), float(-EPSILON),
Vec2f::Zero());
}
if (perim.is_external_perimeter()) {
current_slice_ext_perims_lines.push_back(perim);
}
}
}
// DEBUG EXPORT, NOT USED NOW
// if (BR_bridge) {
// Lines scaledl;
// for (const auto &l : prev_layer_boundary.get_lines()) {
// scaledl.emplace_back(Point::new_scale(l.a), Point::new_scale(l.b));
// }
// Lines perimsl;
// for (const auto &l : current_slice_ext_perims_lines) {
// perimsl.emplace_back(Point::new_scale(l.a), Point::new_scale(l.b));
// }
// BoundingBox bb = get_extents(scaledl);
// bb.merge(get_extents(perimsl));
// ::Slic3r::SVG svg(debug_out_path(
// ("slice" + std::to_string(slice_idx) + "_" + std::to_string(layer_idx).c_str()).c_str()),
// get_extents(scaledl));
// svg.draw(scaledl, "red", scale_(0.4));
// svg.draw(perimsl, "blue", scale_(0.25));
// svg.Close();
// }
for (const auto &l : unstable_lines_per_slice[slice_idx]) {
assert(l.support_point_generated.has_value());
reckon_new_support_point(*l.support_point_generated, create_support_point_position(l.b), float(-EPSILON), Vec2f::Zero());
}
LD current_slice_lines_distancer(current_slice_ext_perims_lines);
LD current_slice_lines_distancer({ext_perim_lines_per_slice[slice_idx].begin(), ext_perim_lines_per_slice[slice_idx].end()});
float unchecked_dist = params.min_distance_between_support_points + 1.0f;
for (const ExtrusionLine &line : current_slice_ext_perims_lines) {
for (const ExtrusionLine &line : current_slice_lines_distancer.get_lines()) {
if ((unchecked_dist + line.len < params.min_distance_between_support_points && line.curled_up_height < params.curling_tolerance_limit) ||
line.len < EPSILON) {
unchecked_dist += line.len;
@ -969,8 +1021,8 @@ std::tuple<SupportPoints, PartialObjects> check_stability(const PrintObject *po,
}
}
}
current_layer_ext_perims_lines.insert(current_layer_ext_perims_lines.end(), current_slice_ext_perims_lines.begin(),
current_slice_ext_perims_lines.end());
current_layer_ext_perims_lines.insert(current_layer_ext_perims_lines.end(), current_slice_lines_distancer.get_lines().begin(),
current_slice_lines_distancer.get_lines().end());
} // slice iterations
prev_layer_ext_perim_lines = LD(current_layer_ext_perims_lines);
} // layer iterations
@ -1024,7 +1076,8 @@ void debug_export(const SupportPoints& support_points,const PartialObjects& obje
std::tuple<SupportPoints, PartialObjects> full_search(const PrintObject *po, const PrintTryCancel& cancel_func, const Params &params)
{
auto results = check_stability(po, cancel_func, params);
auto precomputed_slices_connections = precompute_slices_connections(po);
auto results = check_stability(po, precomputed_slices_connections, cancel_func, params);
#ifdef DEBUG_FILES
auto [supp_points, objects] = results;
debug_export(supp_points, objects, "issues");
@ -1043,7 +1096,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 +1107,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 +1131,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 +1161,42 @@ void estimate_malformations(LayerPtrs &layers, const Params &params)
{
#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 +1205,7 @@ void estimate_malformations(LayerPtrs &layers, const Params &params)
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 +1216,9 @@ void estimate_malformations(LayerPtrs &layers, const Params &params)
fprintf(debug_file, "v %f %f %f %f %f %f\n", line.b[0], line.b[1], l->print_z, color[0], color[1], color[2]);
}
}
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

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