Merge branch 'master' of https://github.com/Prusa-Development/PrusaSlicerPrivate into et_outofbed_optimization
This commit is contained in:
commit
653d7bc678
@ -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)
|
||||
|
@ -1,4 +1,7 @@
|
||||
min_slic3r_version = 2.6.0-alpha5
|
||||
1.9.0-alpha4 Updated XL and MK4 profiles. Updated PC Blend Carbon Fiber density.
|
||||
1.9.0-alpha3 Updated compatibility condition for MMU1 filaments.
|
||||
1.9.0-alpha2 Added profiles for Spectrum filaments.
|
||||
1.9.0-alpha1 Added profiles for Original Prusa MK4.
|
||||
1.9.0-alpha0 Updated output filename format.
|
||||
1.7.0-alpha2 Updated compatibility condition in some filament profiles (Prusa XL).
|
||||
@ -8,7 +11,13 @@ min_slic3r_version = 2.6.0-alpha1
|
||||
1.6.0-alpha2 Added profile for Prusament PETG Carbon Fiber and Fiberthree F3 PA-GF30 Pro. Updated acceleration settings for Prusa MINI.
|
||||
1.6.0-alpha1 Updated FW version notification. Decreased min layer time for PLA.
|
||||
1.6.0-alpha0 Default top fill set to monotonic lines. Updated infill/perimeter overlap values. Updated output filename format. Enabled dynamic overhang speeds.
|
||||
min_slic3r_version = 2.5.2-rc0
|
||||
1.7.3 Updated XL and MK4 profiles. Updated PC Blend Carbon Fiber density.
|
||||
1.7.2 Updated compatibility condition for MMU1 filaments.
|
||||
1.7.1 Added SLA materials. Updated MK4 and XL profiles.
|
||||
1.7.0 Added profiles for Original Prusa MK4.
|
||||
min_slic3r_version = 2.5.1-rc0
|
||||
1.6.4 Fixed compatibility condition for MMU1 filaments.
|
||||
1.6.3 Added SLA materials.
|
||||
1.6.2 Updated compatibility condition in some filament profiles (Prusa XL).
|
||||
1.6.1 Added filament profile for Prusament PETG Tungsten 75%. Updated Prusa XL profiles.
|
||||
|
@ -5,7 +5,7 @@
|
||||
name = Prusa Research
|
||||
# Configuration version of this file. Config file will only be installed, if the config_version differs.
|
||||
# This means, the server may force the PrusaSlicer configuration to be downgraded.
|
||||
config_version = 1.9.0-alpha1
|
||||
config_version = 1.9.0-alpha4
|
||||
# Where to get the updates from?
|
||||
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/
|
||||
changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1%
|
||||
@ -2130,6 +2130,7 @@ default_acceleration = 1250
|
||||
max_print_speed = 200
|
||||
first_layer_extrusion_width = 0.5
|
||||
support_material_extrusion_width = 0.37
|
||||
top_infill_extrusion_width = 0.4
|
||||
gcode_resolution = 0.008
|
||||
compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.4
|
||||
|
||||
@ -2162,6 +2163,7 @@ max_print_speed = 200
|
||||
first_layer_extrusion_width = 0.5
|
||||
gcode_resolution = 0.008
|
||||
support_material_extrusion_width = 0.37
|
||||
top_infill_extrusion_width = 0.4
|
||||
compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.4
|
||||
|
||||
[print:0.20mm SPEED @XL 0.4]
|
||||
@ -2192,6 +2194,7 @@ default_acceleration = 1250
|
||||
max_print_speed = 200
|
||||
first_layer_extrusion_width = 0.5
|
||||
support_material_extrusion_width = 0.37
|
||||
top_infill_extrusion_width = 0.42
|
||||
compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.4
|
||||
|
||||
[print:0.30mm DRAFT @XL 0.4]
|
||||
@ -3036,7 +3039,7 @@ inherits = *0.15mm*; *MK4*
|
||||
perimeter_speed = 45
|
||||
external_perimeter_speed = 25
|
||||
small_perimeter_speed = 25
|
||||
infill_speed = 90
|
||||
infill_speed = 120
|
||||
solid_infill_speed = 90
|
||||
top_solid_infill_speed = 40
|
||||
support_material_contact_distance = 0.2
|
||||
@ -3059,6 +3062,7 @@ max_print_speed = 200
|
||||
first_layer_extrusion_width = 0.5
|
||||
support_material_extrusion_width = 0.37
|
||||
gcode_resolution = 0.008
|
||||
top_infill_extrusion_width = 0.4
|
||||
compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.4
|
||||
|
||||
[print:0.15mm SPEED @MK4 0.4]
|
||||
@ -3089,6 +3093,7 @@ max_print_speed = 200
|
||||
first_layer_extrusion_width = 0.5
|
||||
support_material_extrusion_width = 0.37
|
||||
gcode_resolution = 0.008
|
||||
top_infill_extrusion_width = 0.42
|
||||
compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.4
|
||||
|
||||
[print:0.20mm QUALITY @MK4 0.4]
|
||||
@ -3096,7 +3101,7 @@ inherits = *0.20mm*; *MK4*
|
||||
perimeter_speed = 45
|
||||
external_perimeter_speed = 25
|
||||
small_perimeter_speed = 25
|
||||
infill_speed = 90
|
||||
infill_speed = 120
|
||||
solid_infill_speed = 90
|
||||
top_solid_infill_speed = 40
|
||||
support_material_contact_distance = 0.2
|
||||
@ -3119,6 +3124,7 @@ max_print_speed = 200
|
||||
first_layer_extrusion_width = 0.5
|
||||
gcode_resolution = 0.008
|
||||
support_material_extrusion_width = 0.37
|
||||
top_infill_extrusion_width = 0.4
|
||||
compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.4
|
||||
|
||||
[print:0.20mm SPEED @MK4 0.4]
|
||||
@ -3149,6 +3155,7 @@ default_acceleration = 1000
|
||||
max_print_speed = 200
|
||||
first_layer_extrusion_width = 0.5
|
||||
support_material_extrusion_width = 0.37
|
||||
top_infill_extrusion_width = 0.42
|
||||
compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.4
|
||||
|
||||
[print:0.30mm DRAFT @MK4 0.4]
|
||||
@ -3565,7 +3572,7 @@ compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.8
|
||||
cooling = 1
|
||||
compatible_printers =
|
||||
# For now, all but selected filaments are disabled for the MMU 2.0
|
||||
compatible_printers_condition = ! single_extruder_multi_material and printer_notes!~/.*PG.*/
|
||||
compatible_printers_condition = ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) and printer_notes!~/.*PG.*/
|
||||
end_filament_gcode = "; Filament-specific end gcode"
|
||||
extrusion_multiplier = 1
|
||||
filament_loading_speed = 28
|
||||
@ -3608,7 +3615,7 @@ min_fan_speed = 100
|
||||
temperature = 210
|
||||
slowdown_below_layer_time = 10
|
||||
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.04{else}0.05{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K18{elsif nozzle_diameter[0]==0.8};{else}M900 K30{endif} ; Filament gcode LA 1.0"
|
||||
compatible_printers_condition = ! single_extruder_multi_material and printer_notes!~/.*PG.*/
|
||||
compatible_printers_condition = ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) and printer_notes!~/.*PG.*/
|
||||
|
||||
[filament:*PLAPG*]
|
||||
start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.06{elsif nozzle_diameter[0]==0.25}0.14{elsif nozzle_diameter[0]==0.3}0.08{elsif nozzle_diameter[0]==0.35}0.07{elsif nozzle_diameter[0]==0.6}0.03{elsif nozzle_diameter[0]==0.5}0.035{elsif nozzle_diameter[0]==0.8}0.02{else}0{endif} ; Filament gcode\n\nM142 S36 ; set heatbreak target temp"
|
||||
@ -3658,7 +3665,7 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no
|
||||
temperature = 240
|
||||
filament_retract_length = 1
|
||||
filament_retract_lift = 0.2
|
||||
compatible_printers_condition = printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material
|
||||
compatible_printers_condition = printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:*PET06*]
|
||||
inherits = *PET*
|
||||
@ -3700,7 +3707,7 @@ slowdown_below_layer_time = 18
|
||||
filament_retract_length = 0.8
|
||||
|
||||
[filament:*04PLUS*]
|
||||
compatible_printers_condition = nozzle_diameter[0]>=0.4 and ! single_extruder_multi_material and printer_notes!~/.*PG.*/
|
||||
compatible_printers_condition = nozzle_diameter[0]>=0.4 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) and printer_notes!~/.*PG.*/
|
||||
|
||||
[filament:*04PLUSPG*]
|
||||
compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/
|
||||
@ -3815,7 +3822,7 @@ compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.*
|
||||
|
||||
[filament:*ABSPG*]
|
||||
compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8
|
||||
filament_max_volumetric_speed = 14
|
||||
filament_max_volumetric_speed = 12
|
||||
start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.04{elsif nozzle_diameter[0]==0.25}0.1{elsif nozzle_diameter[0]==0.3}0.06{elsif nozzle_diameter[0]==0.35}0.05{elsif nozzle_diameter[0]==0.5}0.03{elsif nozzle_diameter[0]==0.6}0.02{elsif nozzle_diameter[0]==0.8}0.01{else}0{endif} ; Filament gcode\n\nM142 S40 ; set heatbreak target temp"
|
||||
filament_cooling_final_speed = 50
|
||||
filament_cooling_initial_speed = 10
|
||||
@ -3869,7 +3876,7 @@ compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.6
|
||||
|
||||
[filament:*PC08PG*]
|
||||
inherits = *PCPG*
|
||||
filament_max_volumetric_speed = 20
|
||||
filament_max_volumetric_speed = 18
|
||||
compatible_printers_condition = printer_model=="XL" and nozzle_diameter[0]==0.8
|
||||
|
||||
[filament:*PCMK4*]
|
||||
@ -3885,7 +3892,7 @@ compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.6
|
||||
|
||||
[filament:*PC08MK4*]
|
||||
inherits = *PCMK4*
|
||||
filament_max_volumetric_speed = 20
|
||||
filament_max_volumetric_speed = 18
|
||||
compatible_printers_condition = printer_model=="MK4" and nozzle_diameter[0]==0.8
|
||||
|
||||
[filament:*PAPG*]
|
||||
@ -4196,7 +4203,7 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no
|
||||
temperature = 260
|
||||
filament_retract_length = nil
|
||||
filament_retract_lift = 0.4
|
||||
compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material
|
||||
compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:ColorFabb XT-CF20 @PG]
|
||||
inherits = ColorFabb XT-CF20; *PETPG*; *04PLUSPG*
|
||||
@ -4317,7 +4324,7 @@ filament_colour = #804040
|
||||
filament_max_volumetric_speed = 6
|
||||
first_layer_temperature = 260
|
||||
temperature = 260
|
||||
compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material
|
||||
compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:Kimya ABS Carbon @PG]
|
||||
inherits = Kimya ABS Carbon; *ABSPG*; *04PLUSPG*
|
||||
@ -4577,7 +4584,7 @@ filament_type = PC
|
||||
filament_colour = #DEE0E6
|
||||
filament_max_volumetric_speed = 8
|
||||
filament_retract_lift = 0.2
|
||||
compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material
|
||||
compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.07{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0"
|
||||
|
||||
[filament:Prusament PC Blend @PG]
|
||||
@ -4609,12 +4616,12 @@ inherits = Prusament PC Blend
|
||||
first_layer_bed_temperature = 105
|
||||
bed_temperature = 110
|
||||
disable_fan_first_layers = 6
|
||||
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes=~/.*PRINTER_MODEL_MK(2|2.5).*/ and ! single_extruder_multi_material
|
||||
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes=~/.*PRINTER_MODEL_MK(2|2.5).*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:Prusament PC Blend Carbon Fiber]
|
||||
inherits = Prusament PC Blend
|
||||
filament_cost = 90.73
|
||||
filament_density = 1.16
|
||||
filament_density = 1.22
|
||||
extrusion_multiplier = 1.04
|
||||
first_layer_temperature = 285
|
||||
temperature = 285
|
||||
@ -4623,7 +4630,7 @@ fan_below_layer_time = 10
|
||||
filament_colour = #BBBBBB
|
||||
filament_retract_length = nil
|
||||
filament_retract_lift = nil
|
||||
compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material
|
||||
compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:Prusament PC Blend Carbon Fiber @PG]
|
||||
inherits = Prusament PC Blend Carbon Fiber; *PCPG*
|
||||
@ -4667,7 +4674,7 @@ temperature = 285
|
||||
first_layer_bed_temperature = 90
|
||||
bed_temperature = 115
|
||||
fan_below_layer_time = 10
|
||||
compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material
|
||||
compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:Prusament PA11 Carbon Fiber @PG]
|
||||
inherits = Prusament PA11 Carbon Fiber; *PCPG*
|
||||
@ -4786,7 +4793,7 @@ inherits = *ABSC*
|
||||
filament_vendor = Generic
|
||||
filament_cost = 27.82
|
||||
filament_density = 1.04
|
||||
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material
|
||||
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:Generic ABS @PG]
|
||||
inherits = Generic ABS; *ABSPG*
|
||||
@ -4923,7 +4930,7 @@ renamed_from = "Generic PET"
|
||||
filament_vendor = Generic
|
||||
filament_cost = 27.82
|
||||
filament_density = 1.27
|
||||
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material
|
||||
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:Generic PETG @PG]
|
||||
inherits = Generic PETG; *PETPG*
|
||||
@ -5393,7 +5400,7 @@ filament_retract_lift = 0.4
|
||||
filament_max_volumetric_speed = 4
|
||||
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.08{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0"
|
||||
filament_spool_weight = 0
|
||||
compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and printer_model!="MK2SMM" and ! single_extruder_multi_material
|
||||
compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and printer_model!="MK2SMM" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:addnorth Adura X @PG]
|
||||
inherits = addnorth Adura X; *PETPG*
|
||||
@ -5647,7 +5654,7 @@ filament_retract_length = 1.4
|
||||
filament_max_volumetric_speed = 5
|
||||
filament_spool_weight = 0
|
||||
filament_notes = "Please use a nozzle that is resistant to abrasive filaments, like hardened steel."
|
||||
compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and printer_model!="MK2SMM" and ! single_extruder_multi_material
|
||||
compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and printer_model!="MK2SMM" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:addnorth Rigid X @PG]
|
||||
inherits = addnorth Rigid X; *PETPG*; *04PLUSPG*
|
||||
@ -5693,7 +5700,7 @@ slowdown_below_layer_time = 15
|
||||
min_print_speed = 20
|
||||
filament_spool_weight = 0
|
||||
filament_retract_length = 1
|
||||
compatible_printers_condition = printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material
|
||||
compatible_printers_condition = printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:addnorth Textura @PG]
|
||||
inherits = addnorth Textura; *PLAPG*
|
||||
@ -5730,7 +5737,7 @@ disable_fan_first_layers = 3
|
||||
fan_below_layer_time = 60
|
||||
slowdown_below_layer_time = 15
|
||||
bridge_fan_speed = 20
|
||||
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material
|
||||
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:Filamentworld ABS @PG]
|
||||
inherits = Filamentworld ABS; *ABSPG*
|
||||
@ -5827,7 +5834,7 @@ filament_vendor = Filament PM
|
||||
filament_cost = 27.82
|
||||
filament_density = 1.27
|
||||
filament_spool_weight = 230
|
||||
compatible_printers_condition = nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material
|
||||
compatible_printers_condition = nozzle_diameter[0]!=0.6 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:Filament PM PETG @PG]
|
||||
inherits = Filament PM PETG; *PETPG*
|
||||
@ -5843,7 +5850,7 @@ inherits = *PLA*
|
||||
filament_vendor = Generic
|
||||
filament_cost = 25.4
|
||||
filament_density = 1.24
|
||||
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material
|
||||
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:Generic PLA @PG]
|
||||
inherits = Generic PLA; *PLAPG*
|
||||
@ -6092,6 +6099,220 @@ inherits = Spectrum PLA; *PLA06PG*
|
||||
[filament:Spectrum PLA @PG 0.8]
|
||||
inherits = Spectrum PLA; *PLA08PG*
|
||||
|
||||
[filament:Spectrum PETG Matt]
|
||||
inherits = *PET*
|
||||
filament_vendor = Spectrum
|
||||
bed_temperature = 90
|
||||
bridge_fan_speed = 50
|
||||
extrusion_multiplier = 1.1
|
||||
disable_fan_first_layers = 1
|
||||
full_fan_speed_layer = 1
|
||||
fan_always_on = 1
|
||||
fan_below_layer_time = 20
|
||||
filament_colour = #FF8000
|
||||
filament_max_volumetric_speed = 8
|
||||
filament_type = PETG
|
||||
first_layer_bed_temperature = 85
|
||||
first_layer_temperature = 230
|
||||
max_fan_speed = 100
|
||||
min_fan_speed = 30
|
||||
temperature = 240
|
||||
filament_density = 1.35
|
||||
|
||||
[filament:Spectrum PETG Matt @PG]
|
||||
inherits = Spectrum PETG Matt; *PETPG*
|
||||
|
||||
[filament:Spectrum PETG Matt @PG 0.6]
|
||||
inherits = Spectrum PETG Matt @PG; *PET06PG*
|
||||
|
||||
[filament:Spectrum PETG Matt @PG 0.8]
|
||||
inherits = Spectrum PETG Matt @PG; *PET08PG*
|
||||
|
||||
[filament:Spectrum PETG Matt @MINI]
|
||||
inherits = Spectrum PETG Matt; *PETMINI*
|
||||
|
||||
[filament:Spectrum PETG HT100]
|
||||
inherits = *PET*
|
||||
filament_vendor = Spectrum
|
||||
bed_temperature = 105
|
||||
bridge_fan_speed = 50
|
||||
extrusion_multiplier = 1
|
||||
disable_fan_first_layers = 1
|
||||
full_fan_speed_layer = 1
|
||||
fan_always_on = 1
|
||||
fan_below_layer_time = 20
|
||||
filament_colour = #FF8000
|
||||
filament_max_volumetric_speed = 8
|
||||
filament_type = PETG
|
||||
first_layer_bed_temperature = 105
|
||||
first_layer_temperature = 250
|
||||
max_fan_speed = 100
|
||||
min_fan_speed = 30
|
||||
temperature = 250
|
||||
filament_density = 1.24
|
||||
|
||||
[filament:Spectrum PETG HT100 @PG]
|
||||
inherits = Spectrum PETG HT100; *PETPG*
|
||||
|
||||
[filament:Spectrum PETG HT100 @PG 0.6]
|
||||
inherits = Spectrum PETG HT100 @PG; *PET06PG*
|
||||
|
||||
[filament:Spectrum PETG HT100 @PG 0.8]
|
||||
inherits = Spectrum PETG HT100 @PG; *PET08PG*
|
||||
|
||||
[filament:Spectrum PETG HT100 @MINI]
|
||||
inherits = Spectrum PETG HT100; *PETMINI*
|
||||
bed_temperature = 100
|
||||
first_layer_bed_temperature = 100
|
||||
|
||||
[filament:Spectrum GreenyHT]
|
||||
inherits = *PLA*
|
||||
filament_vendor = Spectrum
|
||||
first_layer_temperature = 205
|
||||
first_layer_bed_temperature = 45
|
||||
temperature = 205
|
||||
bed_temperature = 45
|
||||
bridge_fan_speed = 50
|
||||
extrusion_multiplier = 1.0
|
||||
disable_fan_first_layers = 1
|
||||
full_fan_speed_layer = 1
|
||||
fan_always_on = 1
|
||||
fan_below_layer_time = 20
|
||||
filament_colour = #FF8000
|
||||
filament_max_volumetric_speed = 8
|
||||
filament_type = PLA
|
||||
max_fan_speed = 100
|
||||
min_fan_speed = 30
|
||||
filament_density = 1.54
|
||||
|
||||
[filament:Spectrum GreenyHT @PG]
|
||||
inherits = Spectrum GreenyHT; *PLAPG*
|
||||
|
||||
[filament:Spectrum GreenyHT @PG 0.6]
|
||||
inherits = Spectrum GreenyHT @PG; *PLA06PG*
|
||||
|
||||
[filament:Spectrum GreenyHT @PG 0.8]
|
||||
inherits = Spectrum GreenyHT @PG; *PLA08PG*
|
||||
|
||||
[filament:Spectrum ASA 275]
|
||||
inherits = *ABSC*
|
||||
filament_vendor = Spectrum
|
||||
first_layer_temperature = 237
|
||||
first_layer_bed_temperature = 80
|
||||
temperature = 237
|
||||
bed_temperature = 80
|
||||
extrusion_multiplier = 0.98
|
||||
filament_type = ASA
|
||||
filament_density = 1.24
|
||||
|
||||
[filament:Spectrum ASA 275 @PG]
|
||||
inherits = Spectrum ASA 275; *ABSPG*
|
||||
compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material
|
||||
|
||||
[filament:Spectrum ASA 275 @PG 0.6]
|
||||
inherits = Spectrum ASA 275 @PG; *ABS06PG*
|
||||
compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material
|
||||
|
||||
[filament:Spectrum ASA 275 @PG 0.8]
|
||||
inherits = Spectrum ASA 275 @PG; *ABS08PG*
|
||||
compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material
|
||||
|
||||
[filament:Spectrum ASA 275 @MINI]
|
||||
inherits = Spectrum ASA 275; *ABSMINI*
|
||||
temperature = 235
|
||||
bed_temperature = 80
|
||||
extrusion_multiplier = 1
|
||||
|
||||
[filament:Spectrum ASA Kevlar]
|
||||
inherits = *ABSC*
|
||||
filament_vendor = Spectrum
|
||||
temperature = 250
|
||||
bed_temperature = 105
|
||||
extrusion_multiplier = 1.04
|
||||
filament_type = ASA
|
||||
filament_density = 1.24
|
||||
compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:Spectrum ASA Kevlar @PG]
|
||||
inherits = Spectrum ASA Kevlar; *ABSPG*
|
||||
compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=="XL"
|
||||
|
||||
[filament:Spectrum ASA Kevlar @PG 0.6]
|
||||
inherits = Spectrum ASA Kevlar @PG; *ABS06PG*
|
||||
|
||||
[filament:Spectrum ASA Kevlar @PG 0.8]
|
||||
inherits = Spectrum ASA Kevlar @PG; *ABS08PG*
|
||||
|
||||
[filament:Spectrum ASA Kevlar @MK4]
|
||||
inherits = Spectrum ASA Kevlar; *ABSMK4*
|
||||
compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=="MK4"
|
||||
|
||||
[filament:Spectrum ASA Kevlar @MK4 0.6]
|
||||
inherits = Spectrum ASA Kevlar @MK4; *ABS06MK4*
|
||||
|
||||
[filament:Spectrum ASA Kevlar @MK4 0.8]
|
||||
inherits = Spectrum ASA Kevlar @MK4; *ABS08MK4*
|
||||
|
||||
[filament:Spectrum ASA Kevlar @MINI]
|
||||
inherits = Spectrum ASA Kevlar; *ABSMINI*
|
||||
temperature = 250
|
||||
bed_temperature = 100
|
||||
extrusion_multiplier = 1.03
|
||||
compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model=="MINI"
|
||||
|
||||
[filament:Spectrum Tough PLA]
|
||||
inherits = *PLA*
|
||||
filament_vendor = Spectrum
|
||||
temperature = 235
|
||||
bed_temperature = 45
|
||||
extrusion_multiplier = 0.95
|
||||
filament_type = PLA Tough
|
||||
filament_density = 1.24
|
||||
|
||||
[filament:Spectrum Tough PLA @PG]
|
||||
inherits = Spectrum Tough PLA; *PLAPG*
|
||||
|
||||
[filament:Spectrum Tough PLA @PG 0.6]
|
||||
inherits = Spectrum Tough PLA @PG; *PLA06PG*
|
||||
|
||||
[filament:Spectrum Tough PLA @PG 0.8]
|
||||
inherits = Spectrum Tough PLA @PG; *PLA08PG*
|
||||
|
||||
[filament:Spectrum PLA PRO]
|
||||
inherits = *PLA*
|
||||
filament_vendor = Spectrum
|
||||
filament_type = PLA
|
||||
filament_density = 1.22
|
||||
|
||||
[filament:Spectrum PLA PRO @PG]
|
||||
inherits = Spectrum PLA PRO; *PLAPG*
|
||||
|
||||
[filament:Spectrum PLA PRO @PG 0.6]
|
||||
inherits = Spectrum PLA PRO @PG; *PLA06PG*
|
||||
|
||||
[filament:Spectrum PLA PRO @PG 0.8]
|
||||
inherits = Spectrum PLA PRO @PG; *PLA08PG*
|
||||
|
||||
[filament:Spectrum PCTG]
|
||||
inherits = *PET*
|
||||
filament_vendor = Spectrum
|
||||
filament_type = PCTG
|
||||
temperature = 240
|
||||
bed_temperature = 90
|
||||
filament_density = 1.27
|
||||
|
||||
[filament:Spectrum PCTG @PG]
|
||||
inherits = Spectrum PCTG; *PETPG*
|
||||
|
||||
[filament:Spectrum PCTG @PG 0.6]
|
||||
inherits = Spectrum PCTG @PG; *PET06PG*
|
||||
|
||||
[filament:Spectrum PCTG @PG 0.8]
|
||||
inherits = Spectrum PCTG @PG; *PET08PG*
|
||||
|
||||
[filament:Spectrum PCTG @MINI]
|
||||
inherits = Spectrum PCTG; *PETMINI*
|
||||
|
||||
[filament:Generic FLEX]
|
||||
inherits = *FLEX*
|
||||
filament_vendor = Generic
|
||||
@ -6596,7 +6817,7 @@ extrusion_multiplier = 0.95
|
||||
filament_density = 1.1
|
||||
first_layer_bed_temperature = 105
|
||||
bed_temperature = 100
|
||||
compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material
|
||||
compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:Filatech FilaCarbon @PG]
|
||||
inherits = Filatech FilaCarbon; *ABSPG*; *04PLUSPG*
|
||||
@ -6698,7 +6919,7 @@ first_layer_temperature = 230
|
||||
first_layer_bed_temperature = 100
|
||||
temperature = 225
|
||||
bed_temperature = 110
|
||||
compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material
|
||||
compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:Filatech HIPS @PG]
|
||||
inherits = Filatech HIPS; *ABSPG*
|
||||
@ -6755,7 +6976,7 @@ cooling = 0
|
||||
bridge_fan_speed = 25
|
||||
filament_type = PA
|
||||
filament_max_volumetric_speed = 8
|
||||
compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material
|
||||
compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:Filatech PA @PG]
|
||||
inherits = Filatech PA; *ABSPG*
|
||||
@ -7235,7 +7456,7 @@ min_fan_speed = 20
|
||||
max_fan_speed = 20
|
||||
bridge_fan_speed = 30
|
||||
disable_fan_first_layers = 4
|
||||
compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material
|
||||
compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
filament_notes = "Material Description\nUltrafuse® PC/ABS FR Black is a V-0 flame retardant blend of Polycarbonate and ABS – two of the most used thermoplastics for engineering & electrical applications. The combination of these two materials results in a premium material with a mix of the excellent mechanical properties of PC and the comparably low printing temperature of ABS. Combined with a halogen free flame retardant, parts printed with Ultrafuse® PC/ABS FR Black feature great tensile and impact strength, higher thermal resistance than ABS and can fulfill the requirements of the UL94 V-0 standard.\n\nPrinting Recommendations:\nApply Magigoo PC to a clean build plate to improve adhesion."
|
||||
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.07{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0"
|
||||
|
||||
@ -7670,7 +7891,7 @@ filament_vendor = Made for Prusa
|
||||
filament_cost = 27.82
|
||||
filament_density = 1.08
|
||||
filament_spool_weight = 230
|
||||
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material
|
||||
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:Prusa ABS @PG]
|
||||
inherits = Prusa ABS; *ABSPG*
|
||||
@ -7881,7 +8102,7 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no
|
||||
[filament:Prusament PC Blend Carbon Fiber @MMU2]
|
||||
inherits = Prusament PC Blend @MMU2
|
||||
filament_cost = 90.73
|
||||
filament_density = 1.16
|
||||
filament_density = 1.22
|
||||
extrusion_multiplier = 1.04
|
||||
fan_below_layer_time = 10
|
||||
first_layer_temperature = 280
|
||||
@ -7925,7 +8146,7 @@ max_fan_speed = 20
|
||||
min_fan_speed = 20
|
||||
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.03{else}0.04{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K12{elsif nozzle_diameter[0]==0.8};{else}M900 K20{endif} ; Filament gcode LA 1.0"
|
||||
temperature = 220
|
||||
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material
|
||||
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:Generic HIPS]
|
||||
inherits = *ABS*
|
||||
@ -7945,7 +8166,7 @@ max_fan_speed = 20
|
||||
min_fan_speed = 20
|
||||
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.03{else}0.04{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K12{elsif nozzle_diameter[0]==0.8};{else}M900 K20{endif} ; Filament gcode LA 1.0"
|
||||
temperature = 230
|
||||
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material
|
||||
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:Generic HIPS @PG]
|
||||
inherits = Generic HIPS; *ABSPG*
|
||||
@ -7973,7 +8194,7 @@ filament_vendor = Made for Prusa
|
||||
filament_cost = 27.82
|
||||
filament_density = 1.27
|
||||
filament_spool_weight = 230
|
||||
compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material
|
||||
compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:Prusa PETG @PG]
|
||||
inherits = Prusa PETG; *PETPG*
|
||||
@ -8239,7 +8460,7 @@ filament_vendor = Made for Prusa
|
||||
filament_cost = 27.82
|
||||
filament_density = 1.24
|
||||
filament_spool_weight = 230
|
||||
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material
|
||||
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:Prusa PLA @PG]
|
||||
inherits = Prusa PLA; *PLAPG*
|
||||
@ -9350,7 +9571,7 @@ filament_cost = 36.29
|
||||
filament_density = 1.24
|
||||
filament_spool_weight = 201
|
||||
filament_notes = "Affordable filament for everyday printing in premium quality manufactured in-house by Josef Prusa"
|
||||
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material
|
||||
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:Prusament PLA @PG]
|
||||
inherits = Prusament PLA; *PLAPG*
|
||||
@ -9377,7 +9598,7 @@ filament_max_volumetric_speed = 8
|
||||
filament_type = PVB
|
||||
filament_soluble = 1
|
||||
filament_colour = #FFFF6F
|
||||
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material
|
||||
compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
slowdown_below_layer_time = 20
|
||||
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.05{else}0.08{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0"
|
||||
|
||||
@ -9557,7 +9778,7 @@ temperature = 260
|
||||
max_fan_speed = 0
|
||||
min_fan_speed = 0
|
||||
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.08{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0"
|
||||
compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material
|
||||
compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:Taulman Bridge @PG]
|
||||
inherits = Taulman Bridge; *ABSPG*
|
||||
@ -9779,7 +10000,7 @@ temperature = 285
|
||||
first_layer_bed_temperature = 90
|
||||
bed_temperature = 90
|
||||
fan_below_layer_time = 10
|
||||
compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material
|
||||
compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
max_fan_speed = 15
|
||||
min_fan_speed = 15
|
||||
filament_type = PA
|
||||
@ -10009,7 +10230,7 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no
|
||||
temperature = 235
|
||||
filament_wipe = 0
|
||||
filament_retract_lift = 0
|
||||
compatible_printers_condition = nozzle_diameter[0]>=0.35 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material
|
||||
compatible_printers_condition = nozzle_diameter[0]>=0.35 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
|
||||
|
||||
[filament:FormFutura Centaur PP @PG]
|
||||
inherits = FormFutura Centaur PP; *PETPG*
|
||||
@ -10468,7 +10689,7 @@ compatible_printers_condition = printer_model=="MINI" and nozzle_diameter[0]!=0.
|
||||
[filament:Prusament PC Blend Carbon Fiber @MINI]
|
||||
inherits = Prusament PC Blend @MINI
|
||||
filament_cost = 90.73
|
||||
filament_density = 1.16
|
||||
filament_density = 1.22
|
||||
extrusion_multiplier = 1.04
|
||||
first_layer_temperature = 280
|
||||
temperature = 280
|
||||
|
@ -1,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.
|
||||
|
@ -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
|
||||
|
BIN
resources/profiles/Voron/Voron_SW_thumbnail.png
Normal file
BIN
resources/profiles/Voron/Voron_SW_thumbnail.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
BIN
resources/profiles/Voron/bedtexture-SW-250x210.png
Normal file
BIN
resources/profiles/Voron/bedtexture-SW-250x210.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 460 KiB |
BIN
resources/profiles/Voron/printbed-SW-MK52.stl
Normal file
BIN
resources/profiles/Voron/printbed-SW-MK52.stl
Normal file
Binary file not shown.
@ -8,3 +8,5 @@ add_library(clipper STATIC
|
||||
clipper_z.cpp
|
||||
clipper_z.hpp
|
||||
)
|
||||
|
||||
target_link_libraries(clipper TBB::tbb TBB::tbbmalloc)
|
||||
|
@ -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);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
@ -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);
|
||||
|
@ -24,5 +24,5 @@ set(LIBNEST2D_SRCFILES
|
||||
add_library(libnest2d STATIC ${LIBNEST2D_SRCFILES})
|
||||
|
||||
target_include_directories(libnest2d PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include)
|
||||
target_link_libraries(libnest2d PUBLIC NLopt::nlopt TBB::tbb Boost::boost libslic3r)
|
||||
target_link_libraries(libnest2d PUBLIC NLopt::nlopt TBB::tbb TBB::tbbmalloc Boost::boost libslic3r)
|
||||
target_compile_definitions(libnest2d PUBLIC LIBNEST2D_THREADING_tbb LIBNEST2D_STATIC LIBNEST2D_OPTIMIZER_nlopt LIBNEST2D_GEOMETRIES_libslic3r)
|
||||
|
@ -339,15 +339,15 @@ public:
|
||||
return {distance, nearest_line_index_out, nearest_point_out};
|
||||
}
|
||||
|
||||
template<bool SIGNED_DISTANCE> Floating distance_from_lines(const Vec<2, typename LineType::Scalar> &point) const
|
||||
template<bool SIGNED_DISTANCE> Floating distance_from_lines(const Vec<2, Scalar> &point) const
|
||||
{
|
||||
auto [dist, idx, np] = distance_from_lines_extra<SIGNED_DISTANCE>(point);
|
||||
return dist;
|
||||
}
|
||||
|
||||
std::vector<size_t> all_lines_in_radius(const Vec<2, typename LineType::Scalar> &point, Floating radius)
|
||||
std::vector<size_t> all_lines_in_radius(const Vec<2, Scalar> &point, Floating radius)
|
||||
{
|
||||
return all_lines_in_radius(this->lines, this->tree, point, radius * radius);
|
||||
return AABBTreeLines::all_lines_in_radius(this->lines, this->tree, point.template cast<Floating>(), radius * radius);
|
||||
}
|
||||
|
||||
template<bool sorted> std::vector<std::pair<Vec<2, Scalar>, size_t>> intersections_with_line(const LineType &line) const
|
||||
|
@ -5,7 +5,6 @@
|
||||
|
||||
#include <stack>
|
||||
#include <functional>
|
||||
#include <unordered_set>
|
||||
#include <sstream>
|
||||
#include <queue>
|
||||
#include <functional>
|
||||
@ -181,7 +180,7 @@ void SkeletalTrapezoidation::transferEdge(Point from, Point to, vd_t::edge_type&
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<Point> discretized = discretize(vd_edge, segments);
|
||||
Points discretized = discretize(vd_edge, segments);
|
||||
assert(discretized.size() >= 2);
|
||||
if(discretized.size() < 2)
|
||||
{
|
||||
@ -236,7 +235,7 @@ void SkeletalTrapezoidation::transferEdge(Point from, Point to, vd_t::edge_type&
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Point> SkeletalTrapezoidation::discretize(const vd_t::edge_type& vd_edge, const std::vector<Segment>& segments)
|
||||
Points SkeletalTrapezoidation::discretize(const vd_t::edge_type& vd_edge, const std::vector<Segment>& segments)
|
||||
{
|
||||
/*Terminology in this function assumes that the edge moves horizontally from
|
||||
left to right. This is not necessarily the case; the edge can go in any
|
||||
@ -257,7 +256,7 @@ std::vector<Point> SkeletalTrapezoidation::discretize(const vd_t::edge_type& vd_
|
||||
bool point_right = right_cell->contains_point();
|
||||
if ((!point_left && !point_right) || vd_edge.is_secondary()) // Source vert is directly connected to source segment
|
||||
{
|
||||
return std::vector<Point>({ start, end });
|
||||
return Points({ start, end });
|
||||
}
|
||||
else if (point_left != point_right) //This is a parabolic edge between a point and a line.
|
||||
{
|
||||
@ -311,7 +310,7 @@ std::vector<Point> SkeletalTrapezoidation::discretize(const vd_t::edge_type& vd_
|
||||
//Start generating points along the edge.
|
||||
Point a = start;
|
||||
Point b = end;
|
||||
std::vector<Point> ret;
|
||||
Points ret;
|
||||
ret.emplace_back(a);
|
||||
|
||||
//Introduce an extra edge at the borders of the markings?
|
||||
@ -522,9 +521,11 @@ static bool has_missing_twin_edge(const SkeletalTrapezoidationGraph &graph)
|
||||
return false;
|
||||
}
|
||||
|
||||
inline static void rotate_back_skeletal_trapezoidation_graph_after_fix(SkeletalTrapezoidationGraph &graph,
|
||||
const double fix_angle,
|
||||
const std::unordered_map<Point, Point, PointHash> &vertex_mapping)
|
||||
using PointMap = SkeletalTrapezoidation::PointMap;
|
||||
|
||||
inline static void rotate_back_skeletal_trapezoidation_graph_after_fix(SkeletalTrapezoidationGraph &graph,
|
||||
const double fix_angle,
|
||||
const PointMap &vertex_mapping)
|
||||
{
|
||||
for (STHalfEdgeNode &node : graph.nodes) {
|
||||
// If a mapping exists between a rotated point and an original point, use this mapping. Otherwise, rotate a point in the opposite direction.
|
||||
@ -588,7 +589,7 @@ VoronoiDiagramStatus detect_voronoi_diagram_known_issues(const Geometry::Voronoi
|
||||
return VoronoiDiagramStatus::NO_ISSUE_DETECTED;
|
||||
}
|
||||
|
||||
inline static std::pair<std::unordered_map<Point, Point, PointHash>, double> try_to_fix_degenerated_voronoi_diagram_by_rotation(
|
||||
inline static std::pair<PointMap, double> try_to_fix_degenerated_voronoi_diagram_by_rotation(
|
||||
Geometry::VoronoiDiagram &voronoi_diagram,
|
||||
const Polygons &polys,
|
||||
Polygons &polys_rotated,
|
||||
@ -597,7 +598,7 @@ inline static std::pair<std::unordered_map<Point, Point, PointHash>, double> try
|
||||
{
|
||||
const Polygons polys_rotated_original = polys_rotated;
|
||||
double fixed_by_angle = fix_angles.front();
|
||||
std::unordered_map<Point, Point, PointHash> vertex_mapping;
|
||||
PointMap vertex_mapping;
|
||||
|
||||
for (const double &fix_angle : fix_angles) {
|
||||
vertex_mapping.clear();
|
||||
@ -685,7 +686,7 @@ void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys)
|
||||
const std::vector<double> fix_angles = {PI / 6, PI / 5, PI / 7, PI / 11};
|
||||
double fixed_by_angle = fix_angles.front();
|
||||
|
||||
std::unordered_map<Point, Point, PointHash> vertex_mapping;
|
||||
PointMap vertex_mapping;
|
||||
// polys_copy is referenced through items stored in the std::vector segments.
|
||||
Polygons polys_copy = polys;
|
||||
if (status != VoronoiDiagramStatus::NO_ISSUE_DETECTED) {
|
||||
@ -813,9 +814,11 @@ process_voronoi_diagram:
|
||||
edge.from->incident_edge = &edge;
|
||||
}
|
||||
|
||||
using NodeSet = SkeletalTrapezoidation::NodeSet;
|
||||
|
||||
void SkeletalTrapezoidation::separatePointyQuadEndNodes()
|
||||
{
|
||||
std::unordered_set<node_t*> visited_nodes;
|
||||
NodeSet visited_nodes;
|
||||
for (edge_t& edge : graph.edges)
|
||||
{
|
||||
if (edge.prev)
|
||||
@ -2285,16 +2288,18 @@ void SkeletalTrapezoidation::addToolpathSegment(const ExtrusionJunction& from, c
|
||||
|
||||
void SkeletalTrapezoidation::connectJunctions(ptr_vector_t<LineJunctions>& edge_junctions)
|
||||
{
|
||||
std::unordered_set<edge_t*> unprocessed_quad_starts(graph.edges.size() * 5 / 2);
|
||||
using EdgeSet = ankerl::unordered_dense::set<edge_t*>;
|
||||
|
||||
EdgeSet unprocessed_quad_starts(graph.edges.size() * 5 / 2);
|
||||
for (edge_t& edge : graph.edges)
|
||||
{
|
||||
if (!edge.prev)
|
||||
{
|
||||
unprocessed_quad_starts.insert(&edge);
|
||||
unprocessed_quad_starts.emplace(&edge);
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_set<edge_t*> passed_odd_edges;
|
||||
EdgeSet passed_odd_edges;
|
||||
|
||||
while (!unprocessed_quad_starts.empty())
|
||||
{
|
||||
|
@ -7,8 +7,10 @@
|
||||
#include <boost/polygon/voronoi.hpp>
|
||||
|
||||
#include <memory> // smart pointers
|
||||
#include <unordered_map>
|
||||
#include <utility> // pair
|
||||
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include <Arachne/utils/VoronoiUtils.hpp>
|
||||
|
||||
#include "utils/HalfEdgeGraph.hpp"
|
||||
@ -80,7 +82,9 @@ class SkeletalTrapezoidation
|
||||
const BeadingStrategy& beading_strategy;
|
||||
|
||||
public:
|
||||
using Segment = PolygonsSegmentIndex;
|
||||
using Segment = PolygonsSegmentIndex;
|
||||
using PointMap = ankerl::unordered_dense::map<Point, Point, PointHash>;
|
||||
using NodeSet = ankerl::unordered_dense::set<node_t*>;
|
||||
|
||||
/*!
|
||||
* Construct a new trapezoidation problem to solve.
|
||||
@ -164,8 +168,8 @@ protected:
|
||||
* mapping each voronoi VD edge to the corresponding halfedge HE edge
|
||||
* In case the result segment is discretized, we map the VD edge to the *last* HE edge
|
||||
*/
|
||||
std::unordered_map<vd_t::edge_type*, edge_t*> vd_edge_to_he_edge;
|
||||
std::unordered_map<vd_t::vertex_type*, node_t*> vd_node_to_he_node;
|
||||
ankerl::unordered_dense::map<vd_t::edge_type*, edge_t*> vd_edge_to_he_edge;
|
||||
ankerl::unordered_dense::map<vd_t::vertex_type*, node_t*> vd_node_to_he_node;
|
||||
node_t& makeNode(vd_t::vertex_type& vd_node, Point p); //!< Get the node which the VD node maps to, or create a new mapping if there wasn't any yet.
|
||||
|
||||
/*!
|
||||
@ -204,7 +208,7 @@ protected:
|
||||
* \return A number of coordinates along the edge where the edge is broken
|
||||
* up into discrete pieces.
|
||||
*/
|
||||
std::vector<Point> discretize(const vd_t::edge_type& segment, const std::vector<Segment>& segments);
|
||||
Points discretize(const vd_t::edge_type& segment, const std::vector<Segment>& segments);
|
||||
|
||||
/*!
|
||||
* Compute the range of line segments that surround a cell of the skeletal
|
||||
|
@ -2,7 +2,8 @@
|
||||
//CuraEngine is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
#include "SkeletalTrapezoidationGraph.hpp"
|
||||
#include <unordered_map>
|
||||
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
@ -180,8 +181,8 @@ bool STHalfEdgeNode::isLocalMaximum(bool strict) const
|
||||
|
||||
void SkeletalTrapezoidationGraph::collapseSmallEdges(coord_t snap_dist)
|
||||
{
|
||||
std::unordered_map<edge_t*, std::list<edge_t>::iterator> edge_locator;
|
||||
std::unordered_map<node_t*, std::list<node_t>::iterator> node_locator;
|
||||
ankerl::unordered_dense::map<edge_t*, Edges::iterator> edge_locator;
|
||||
ankerl::unordered_dense::map<node_t*, Nodes::iterator> node_locator;
|
||||
|
||||
for (auto edge_it = edges.begin(); edge_it != edges.end(); ++edge_it)
|
||||
{
|
||||
@ -193,7 +194,7 @@ void SkeletalTrapezoidationGraph::collapseSmallEdges(coord_t snap_dist)
|
||||
node_locator.emplace(&*node_it, node_it);
|
||||
}
|
||||
|
||||
auto safelyRemoveEdge = [this, &edge_locator](edge_t* to_be_removed, std::list<edge_t>::iterator& current_edge_it, bool& edge_it_is_updated)
|
||||
auto safelyRemoveEdge = [this, &edge_locator](edge_t* to_be_removed, Edges::iterator& current_edge_it, bool& edge_it_is_updated)
|
||||
{
|
||||
if (current_edge_it != edges.end()
|
||||
&& to_be_removed == &*current_edge_it)
|
||||
|
@ -2,7 +2,6 @@
|
||||
// CuraEngine is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
#include <algorithm> //For std::partition_copy and std::min_element.
|
||||
#include <unordered_set>
|
||||
|
||||
#include "WallToolPaths.hpp"
|
||||
|
||||
@ -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,
|
||||
|
@ -5,7 +5,8 @@
|
||||
#define CURAENGINE_WALLTOOLPATHS_H
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include "BeadingStrategy/BeadingStrategyFactory.hpp"
|
||||
#include "utils/ExtrusionLine.hpp"
|
||||
@ -73,6 +74,7 @@ public:
|
||||
*/
|
||||
static bool removeEmptyToolPaths(std::vector<VariableWidthLines> &toolpaths);
|
||||
|
||||
using ExtrusionLineSet = ankerl::unordered_dense::set<std::pair<const ExtrusionLine *, const ExtrusionLine *>, boost::hash<std::pair<const ExtrusionLine *, const ExtrusionLine *>>>;
|
||||
/*!
|
||||
* Get the order constraints of the insets when printing walls per region / hole.
|
||||
* Each returned pair consists of adjacent wall lines where the left has an inset_idx one lower than the right.
|
||||
@ -81,7 +83,7 @@ public:
|
||||
*
|
||||
* \param outer_to_inner Whether the wall polygons with a lower inset_idx should go before those with a higher one.
|
||||
*/
|
||||
static std::unordered_set<std::pair<const ExtrusionLine *, const ExtrusionLine *>, boost::hash<std::pair<const ExtrusionLine *, const ExtrusionLine *>>> getRegionOrder(const std::vector<ExtrusionLine *> &input, bool outer_to_inner);
|
||||
static ExtrusionLineSet getRegionOrder(const std::vector<ExtrusionLine *> &input, bool outer_to_inner);
|
||||
|
||||
protected:
|
||||
/*!
|
||||
|
@ -21,8 +21,10 @@ class HalfEdgeGraph
|
||||
public:
|
||||
using edge_t = derived_edge_t;
|
||||
using node_t = derived_node_t;
|
||||
std::list<edge_t> edges;
|
||||
std::list<node_t> nodes;
|
||||
using Edges = std::list<edge_t>;
|
||||
using Nodes = std::list<node_t>;
|
||||
Edges edges;
|
||||
Nodes nodes;
|
||||
};
|
||||
|
||||
} // namespace Slic3r::Arachne
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include "SparsePointGrid.hpp"
|
||||
#include "PolygonsPointIndex.hpp"
|
||||
#include "../../Polygon.hpp"
|
||||
#include <unordered_set>
|
||||
#include <cassert>
|
||||
|
||||
namespace Slic3r::Arachne
|
||||
|
@ -6,7 +6,6 @@
|
||||
#define UTILS_SPARSE_GRID_H
|
||||
|
||||
#include <cassert>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
|
@ -6,7 +6,6 @@
|
||||
#define UTILS_SPARSE_LINE_GRID_H
|
||||
|
||||
#include <cassert>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
|
@ -6,7 +6,6 @@
|
||||
#define UTILS_SPARSE_POINT_GRID_H
|
||||
|
||||
#include <cassert>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "SparseGrid.hpp"
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include "../../Point.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
|
@ -138,9 +138,9 @@ public:
|
||||
return Point(coord_t(p.x() * matrix[0] + p.y() * matrix[2]), coord_t(p.x() * matrix[1] + p.y() * matrix[3]));
|
||||
}
|
||||
};
|
||||
std::vector<Point> VoronoiUtils::discretizeParabola(const Point& p, const Segment& segment, Point s, Point e, coord_t approximate_step_size, float transitioning_angle)
|
||||
Points VoronoiUtils::discretizeParabola(const Point& p, const Segment& segment, Point s, Point e, coord_t approximate_step_size, float transitioning_angle)
|
||||
{
|
||||
std::vector<Point> discretized;
|
||||
Points discretized;
|
||||
// x is distance of point projected on the segment ab
|
||||
// xx is point projected on the segment ab
|
||||
const Point a = segment.from();
|
||||
|
@ -34,7 +34,7 @@ public:
|
||||
* Discretize a parabola based on (approximate) step size.
|
||||
* The \p approximate_step_size is measured parallel to the \p source_segment, not along the parabola.
|
||||
*/
|
||||
static std::vector<Point> discretizeParabola(const Point &source_point, const Segment &source_segment, Point start, Point end, coord_t approximate_step_size, float transitioning_angle);
|
||||
static Points discretizeParabola(const Point &source_point, const Segment &source_segment, Point start, Point end, coord_t approximate_step_size, float transitioning_angle);
|
||||
|
||||
static inline bool is_finite(const VoronoiUtils::vd_t::vertex_type &vertex)
|
||||
{
|
||||
|
@ -6,23 +6,19 @@
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
template BoundingBoxBase<Point>::BoundingBoxBase(const std::vector<Point> &points);
|
||||
template BoundingBoxBase<Point, Points>::BoundingBoxBase(const Points &points);
|
||||
template BoundingBoxBase<Vec2d>::BoundingBoxBase(const std::vector<Vec2d> &points);
|
||||
|
||||
template BoundingBox3Base<Vec3d>::BoundingBox3Base(const std::vector<Vec3d> &points);
|
||||
|
||||
void BoundingBox::polygon(Polygon* polygon) const
|
||||
{
|
||||
polygon->points.clear();
|
||||
polygon->points.resize(4);
|
||||
polygon->points[0](0) = this->min(0);
|
||||
polygon->points[0](1) = this->min(1);
|
||||
polygon->points[1](0) = this->max(0);
|
||||
polygon->points[1](1) = this->min(1);
|
||||
polygon->points[2](0) = this->max(0);
|
||||
polygon->points[2](1) = this->max(1);
|
||||
polygon->points[3](0) = this->min(0);
|
||||
polygon->points[3](1) = this->max(1);
|
||||
polygon->points = {
|
||||
this->min,
|
||||
{ this->max.x(), this->min.y() },
|
||||
this->max,
|
||||
{ this->min.x(), this->max.y() }
|
||||
};
|
||||
}
|
||||
|
||||
Polygon BoundingBox::polygon() const
|
||||
@ -37,8 +33,8 @@ BoundingBox BoundingBox::rotated(double angle) const
|
||||
BoundingBox out;
|
||||
out.merge(this->min.rotated(angle));
|
||||
out.merge(this->max.rotated(angle));
|
||||
out.merge(Point(this->min(0), this->max(1)).rotated(angle));
|
||||
out.merge(Point(this->max(0), this->min(1)).rotated(angle));
|
||||
out.merge(Point(this->min.x(), this->max.y()).rotated(angle));
|
||||
out.merge(Point(this->max.x(), this->min.y()).rotated(angle));
|
||||
return out;
|
||||
}
|
||||
|
||||
@ -47,23 +43,23 @@ BoundingBox BoundingBox::rotated(double angle, const Point ¢er) const
|
||||
BoundingBox out;
|
||||
out.merge(this->min.rotated(angle, center));
|
||||
out.merge(this->max.rotated(angle, center));
|
||||
out.merge(Point(this->min(0), this->max(1)).rotated(angle, center));
|
||||
out.merge(Point(this->max(0), this->min(1)).rotated(angle, center));
|
||||
out.merge(Point(this->min.x(), this->max.y()).rotated(angle, center));
|
||||
out.merge(Point(this->max.x(), this->min.y()).rotated(angle, center));
|
||||
return out;
|
||||
}
|
||||
|
||||
template <class PointClass> void
|
||||
BoundingBoxBase<PointClass>::scale(double factor)
|
||||
template <class PointType, typename APointsType> void
|
||||
BoundingBoxBase<PointType, APointsType>::scale(double factor)
|
||||
{
|
||||
this->min *= factor;
|
||||
this->max *= factor;
|
||||
}
|
||||
template void BoundingBoxBase<Point>::scale(double factor);
|
||||
template void BoundingBoxBase<Point, Points>::scale(double factor);
|
||||
template void BoundingBoxBase<Vec2d>::scale(double factor);
|
||||
template void BoundingBoxBase<Vec3d>::scale(double factor);
|
||||
|
||||
template <class PointClass> void
|
||||
BoundingBoxBase<PointClass>::merge(const PointClass &point)
|
||||
template <class PointType, typename APointsType> void
|
||||
BoundingBoxBase<PointType, APointsType>::merge(const PointType &point)
|
||||
{
|
||||
if (this->defined) {
|
||||
this->min = this->min.cwiseMin(point);
|
||||
@ -74,22 +70,22 @@ BoundingBoxBase<PointClass>::merge(const PointClass &point)
|
||||
this->defined = true;
|
||||
}
|
||||
}
|
||||
template void BoundingBoxBase<Point>::merge(const Point &point);
|
||||
template void BoundingBoxBase<Point, Points>::merge(const Point &point);
|
||||
template void BoundingBoxBase<Vec2f>::merge(const Vec2f &point);
|
||||
template void BoundingBoxBase<Vec2d>::merge(const Vec2d &point);
|
||||
|
||||
template <class PointClass> void
|
||||
BoundingBoxBase<PointClass>::merge(const std::vector<PointClass> &points)
|
||||
template <class PointType, typename APointsType> void
|
||||
BoundingBoxBase<PointType, APointsType>::merge(const PointsType &points)
|
||||
{
|
||||
this->merge(BoundingBoxBase(points));
|
||||
}
|
||||
template void BoundingBoxBase<Point>::merge(const Points &points);
|
||||
template void BoundingBoxBase<Point, Points>::merge(const Points &points);
|
||||
template void BoundingBoxBase<Vec2d>::merge(const Pointfs &points);
|
||||
|
||||
template <class PointClass> void
|
||||
BoundingBoxBase<PointClass>::merge(const BoundingBoxBase<PointClass> &bb)
|
||||
template <class PointType, typename APointsType> void
|
||||
BoundingBoxBase<PointType, APointsType>::merge(const BoundingBoxBase<PointType, PointsType> &bb)
|
||||
{
|
||||
assert(bb.defined || bb.min(0) >= bb.max(0) || bb.min(1) >= bb.max(1));
|
||||
assert(bb.defined || bb.min.x() >= bb.max.x() || bb.min.y() >= bb.max.y());
|
||||
if (bb.defined) {
|
||||
if (this->defined) {
|
||||
this->min = this->min.cwiseMin(bb.min);
|
||||
@ -101,12 +97,12 @@ BoundingBoxBase<PointClass>::merge(const BoundingBoxBase<PointClass> &bb)
|
||||
}
|
||||
}
|
||||
}
|
||||
template void BoundingBoxBase<Point>::merge(const BoundingBoxBase<Point> &bb);
|
||||
template void BoundingBoxBase<Point, Points>::merge(const BoundingBoxBase<Point, Points> &bb);
|
||||
template void BoundingBoxBase<Vec2f>::merge(const BoundingBoxBase<Vec2f> &bb);
|
||||
template void BoundingBoxBase<Vec2d>::merge(const BoundingBoxBase<Vec2d> &bb);
|
||||
|
||||
template <class PointClass> void
|
||||
BoundingBox3Base<PointClass>::merge(const PointClass &point)
|
||||
template <class PointType> void
|
||||
BoundingBox3Base<PointType>::merge(const PointType &point)
|
||||
{
|
||||
if (this->defined) {
|
||||
this->min = this->min.cwiseMin(point);
|
||||
@ -120,17 +116,17 @@ BoundingBox3Base<PointClass>::merge(const PointClass &point)
|
||||
template void BoundingBox3Base<Vec3f>::merge(const Vec3f &point);
|
||||
template void BoundingBox3Base<Vec3d>::merge(const Vec3d &point);
|
||||
|
||||
template <class PointClass> void
|
||||
BoundingBox3Base<PointClass>::merge(const std::vector<PointClass> &points)
|
||||
template <class PointType> void
|
||||
BoundingBox3Base<PointType>::merge(const PointsType &points)
|
||||
{
|
||||
this->merge(BoundingBox3Base(points));
|
||||
}
|
||||
template void BoundingBox3Base<Vec3d>::merge(const Pointf3s &points);
|
||||
|
||||
template <class PointClass> void
|
||||
BoundingBox3Base<PointClass>::merge(const BoundingBox3Base<PointClass> &bb)
|
||||
template <class PointType> void
|
||||
BoundingBox3Base<PointType>::merge(const BoundingBox3Base<PointType> &bb)
|
||||
{
|
||||
assert(bb.defined || bb.min(0) >= bb.max(0) || bb.min(1) >= bb.max(1) || bb.min(2) >= bb.max(2));
|
||||
assert(bb.defined || bb.min.x() >= bb.max.x() || bb.min.y() >= bb.max.y() || bb.min.z() >= bb.max.z());
|
||||
if (bb.defined) {
|
||||
if (this->defined) {
|
||||
this->min = this->min.cwiseMin(bb.min);
|
||||
@ -144,83 +140,78 @@ BoundingBox3Base<PointClass>::merge(const BoundingBox3Base<PointClass> &bb)
|
||||
}
|
||||
template void BoundingBox3Base<Vec3d>::merge(const BoundingBox3Base<Vec3d> &bb);
|
||||
|
||||
template <class PointClass> PointClass
|
||||
BoundingBoxBase<PointClass>::size() const
|
||||
template <class PointType, typename APointsType> PointType
|
||||
BoundingBoxBase<PointType, APointsType>::size() const
|
||||
{
|
||||
return PointClass(this->max(0) - this->min(0), this->max(1) - this->min(1));
|
||||
return this->max - this->min;
|
||||
}
|
||||
template Point BoundingBoxBase<Point>::size() const;
|
||||
template Point BoundingBoxBase<Point, Points>::size() const;
|
||||
template Vec2f BoundingBoxBase<Vec2f>::size() const;
|
||||
template Vec2d BoundingBoxBase<Vec2d>::size() const;
|
||||
|
||||
template <class PointClass> PointClass
|
||||
BoundingBox3Base<PointClass>::size() const
|
||||
template <class PointType> PointType
|
||||
BoundingBox3Base<PointType>::size() const
|
||||
{
|
||||
return PointClass(this->max(0) - this->min(0), this->max(1) - this->min(1), this->max(2) - this->min(2));
|
||||
return this->max - this->min;
|
||||
}
|
||||
template Vec3f BoundingBox3Base<Vec3f>::size() const;
|
||||
template Vec3d BoundingBox3Base<Vec3d>::size() const;
|
||||
|
||||
template <class PointClass> double BoundingBoxBase<PointClass>::radius() const
|
||||
template <class PointType, typename APointsType> double BoundingBoxBase<PointType, APointsType>::radius() const
|
||||
{
|
||||
assert(this->defined);
|
||||
double x = this->max(0) - this->min(0);
|
||||
double y = this->max(1) - this->min(1);
|
||||
return 0.5 * sqrt(x*x+y*y);
|
||||
return 0.5 * (this->max - this->min).template cast<double>().norm();
|
||||
}
|
||||
template double BoundingBoxBase<Point>::radius() const;
|
||||
template double BoundingBoxBase<Point, Points>::radius() const;
|
||||
template double BoundingBoxBase<Vec2d>::radius() const;
|
||||
|
||||
template <class PointClass> double BoundingBox3Base<PointClass>::radius() const
|
||||
template <class PointType> double BoundingBox3Base<PointType>::radius() const
|
||||
{
|
||||
double x = this->max(0) - this->min(0);
|
||||
double y = this->max(1) - this->min(1);
|
||||
double z = this->max(2) - this->min(2);
|
||||
return 0.5 * sqrt(x*x+y*y+z*z);
|
||||
return 0.5 * (this->max - this->min).template cast<double>().norm();
|
||||
}
|
||||
template double BoundingBox3Base<Vec3d>::radius() const;
|
||||
|
||||
template <class PointClass> void
|
||||
BoundingBoxBase<PointClass>::offset(coordf_t delta)
|
||||
template <class PointType, typename APointsType> void
|
||||
BoundingBoxBase<PointType, APointsType>::offset(coordf_t delta)
|
||||
{
|
||||
PointClass v(delta, delta);
|
||||
PointType v(delta, delta);
|
||||
this->min -= v;
|
||||
this->max += v;
|
||||
}
|
||||
template void BoundingBoxBase<Point>::offset(coordf_t delta);
|
||||
template void BoundingBoxBase<Point, Points>::offset(coordf_t delta);
|
||||
template void BoundingBoxBase<Vec2d>::offset(coordf_t delta);
|
||||
|
||||
template <class PointClass> void
|
||||
BoundingBox3Base<PointClass>::offset(coordf_t delta)
|
||||
template <class PointType> void
|
||||
BoundingBox3Base<PointType>::offset(coordf_t delta)
|
||||
{
|
||||
PointClass v(delta, delta, delta);
|
||||
PointType v(delta, delta, delta);
|
||||
this->min -= v;
|
||||
this->max += v;
|
||||
}
|
||||
template void BoundingBox3Base<Vec3d>::offset(coordf_t delta);
|
||||
|
||||
template <class PointClass> PointClass
|
||||
BoundingBoxBase<PointClass>::center() const
|
||||
template <class PointType, typename APointsType> PointType
|
||||
BoundingBoxBase<PointType, APointsType>::center() const
|
||||
{
|
||||
return (this->min + this->max) / 2;
|
||||
}
|
||||
template Point BoundingBoxBase<Point>::center() const;
|
||||
template Point BoundingBoxBase<Point, Points>::center() const;
|
||||
template Vec2f BoundingBoxBase<Vec2f>::center() const;
|
||||
template Vec2d BoundingBoxBase<Vec2d>::center() const;
|
||||
|
||||
template <class PointClass> PointClass
|
||||
BoundingBox3Base<PointClass>::center() const
|
||||
template <class PointType> PointType
|
||||
BoundingBox3Base<PointType>::center() const
|
||||
{
|
||||
return (this->min + this->max) / 2;
|
||||
}
|
||||
template Vec3f BoundingBox3Base<Vec3f>::center() const;
|
||||
template Vec3d BoundingBox3Base<Vec3d>::center() const;
|
||||
|
||||
template <class PointClass> coordf_t
|
||||
BoundingBox3Base<PointClass>::max_size() const
|
||||
template <class PointType> coordf_t
|
||||
BoundingBox3Base<PointType>::max_size() const
|
||||
{
|
||||
PointClass s = size();
|
||||
return std::max(s(0), std::max(s(1), s(2)));
|
||||
PointType s = size();
|
||||
return std::max(s.x(), std::max(s.y(), s.z()));
|
||||
}
|
||||
template coordf_t BoundingBox3Base<Vec3f>::max_size() const;
|
||||
template coordf_t BoundingBox3Base<Vec3d>::max_size() const;
|
||||
@ -228,8 +219,8 @@ template coordf_t BoundingBox3Base<Vec3d>::max_size() const;
|
||||
void BoundingBox::align_to_grid(const coord_t cell_size)
|
||||
{
|
||||
if (this->defined) {
|
||||
min(0) = Slic3r::align_to_grid(min(0), cell_size);
|
||||
min(1) = Slic3r::align_to_grid(min(1), cell_size);
|
||||
min.x() = Slic3r::align_to_grid(min.x(), cell_size);
|
||||
min.y() = Slic3r::align_to_grid(min.y(), cell_size);
|
||||
}
|
||||
}
|
||||
|
||||
@ -238,14 +229,14 @@ BoundingBoxf3 BoundingBoxf3::transformed(const Transform3d& matrix) const
|
||||
typedef Eigen::Matrix<double, 3, 8, Eigen::DontAlign> Vertices;
|
||||
|
||||
Vertices src_vertices;
|
||||
src_vertices(0, 0) = min(0); src_vertices(1, 0) = min(1); src_vertices(2, 0) = min(2);
|
||||
src_vertices(0, 1) = max(0); src_vertices(1, 1) = min(1); src_vertices(2, 1) = min(2);
|
||||
src_vertices(0, 2) = max(0); src_vertices(1, 2) = max(1); src_vertices(2, 2) = min(2);
|
||||
src_vertices(0, 3) = min(0); src_vertices(1, 3) = max(1); src_vertices(2, 3) = min(2);
|
||||
src_vertices(0, 4) = min(0); src_vertices(1, 4) = min(1); src_vertices(2, 4) = max(2);
|
||||
src_vertices(0, 5) = max(0); src_vertices(1, 5) = min(1); src_vertices(2, 5) = max(2);
|
||||
src_vertices(0, 6) = max(0); src_vertices(1, 6) = max(1); src_vertices(2, 6) = max(2);
|
||||
src_vertices(0, 7) = min(0); src_vertices(1, 7) = max(1); src_vertices(2, 7) = max(2);
|
||||
src_vertices(0, 0) = min.x(); src_vertices(1, 0) = min.y(); src_vertices(2, 0) = min.z();
|
||||
src_vertices(0, 1) = max.x(); src_vertices(1, 1) = min.y(); src_vertices(2, 1) = min.z();
|
||||
src_vertices(0, 2) = max.x(); src_vertices(1, 2) = max.y(); src_vertices(2, 2) = min.z();
|
||||
src_vertices(0, 3) = min.x(); src_vertices(1, 3) = max.y(); src_vertices(2, 3) = min.z();
|
||||
src_vertices(0, 4) = min.x(); src_vertices(1, 4) = min.y(); src_vertices(2, 4) = max.z();
|
||||
src_vertices(0, 5) = max.x(); src_vertices(1, 5) = min.y(); src_vertices(2, 5) = max.z();
|
||||
src_vertices(0, 6) = max.x(); src_vertices(1, 6) = max.y(); src_vertices(2, 6) = max.z();
|
||||
src_vertices(0, 7) = min.x(); src_vertices(1, 7) = max.y(); src_vertices(2, 7) = max.z();
|
||||
|
||||
Vertices dst_vertices = matrix * src_vertices.colwise().homogeneous();
|
||||
|
||||
|
@ -8,53 +8,54 @@
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
template <class PointClass>
|
||||
template <typename PointType, typename APointsType = std::vector<PointType>>
|
||||
class BoundingBoxBase
|
||||
{
|
||||
public:
|
||||
PointClass min;
|
||||
PointClass max;
|
||||
using PointsType = APointsType;
|
||||
PointType min;
|
||||
PointType max;
|
||||
bool defined;
|
||||
|
||||
BoundingBoxBase() : min(PointClass::Zero()), max(PointClass::Zero()), defined(false) {}
|
||||
BoundingBoxBase(const PointClass &pmin, const PointClass &pmax) :
|
||||
BoundingBoxBase() : min(PointType::Zero()), max(PointType::Zero()), defined(false) {}
|
||||
BoundingBoxBase(const PointType &pmin, const PointType &pmax) :
|
||||
min(pmin), max(pmax), defined(pmin.x() < pmax.x() && pmin.y() < pmax.y()) {}
|
||||
BoundingBoxBase(const PointClass &p1, const PointClass &p2, const PointClass &p3) :
|
||||
BoundingBoxBase(const PointType &p1, const PointType &p2, const PointType &p3) :
|
||||
min(p1), max(p1), defined(false) { merge(p2); merge(p3); }
|
||||
|
||||
template<class It, class = IteratorOnly<It>>
|
||||
BoundingBoxBase(It from, It to)
|
||||
{ construct(*this, from, to); }
|
||||
|
||||
BoundingBoxBase(const std::vector<PointClass> &points)
|
||||
BoundingBoxBase(const PointsType &points)
|
||||
: BoundingBoxBase(points.begin(), points.end())
|
||||
{}
|
||||
|
||||
void reset() { this->defined = false; this->min = PointClass::Zero(); this->max = PointClass::Zero(); }
|
||||
void merge(const PointClass &point);
|
||||
void merge(const std::vector<PointClass> &points);
|
||||
void merge(const BoundingBoxBase<PointClass> &bb);
|
||||
void reset() { this->defined = false; this->min = PointType::Zero(); this->max = PointType::Zero(); }
|
||||
void merge(const PointType &point);
|
||||
void merge(const PointsType &points);
|
||||
void merge(const BoundingBoxBase<PointType, PointsType> &bb);
|
||||
void scale(double factor);
|
||||
PointClass size() const;
|
||||
PointType size() const;
|
||||
double radius() const;
|
||||
void translate(coordf_t x, coordf_t y) { assert(this->defined); PointClass v(x, y); this->min += v; this->max += v; }
|
||||
void translate(const PointClass &v) { this->min += v; this->max += v; }
|
||||
void translate(coordf_t x, coordf_t y) { assert(this->defined); PointType v(x, y); this->min += v; this->max += v; }
|
||||
void translate(const PointType &v) { this->min += v; this->max += v; }
|
||||
void offset(coordf_t delta);
|
||||
BoundingBoxBase<PointClass> inflated(coordf_t delta) const throw() { BoundingBoxBase<PointClass> out(*this); out.offset(delta); return out; }
|
||||
PointClass center() const;
|
||||
bool contains(const PointClass &point) const {
|
||||
BoundingBoxBase<PointType, PointsType> inflated(coordf_t delta) const throw() { BoundingBoxBase<PointType, PointsType> out(*this); out.offset(delta); return out; }
|
||||
PointType center() const;
|
||||
bool contains(const PointType &point) const {
|
||||
return point.x() >= this->min.x() && point.x() <= this->max.x()
|
||||
&& point.y() >= this->min.y() && point.y() <= this->max.y();
|
||||
}
|
||||
bool contains(const BoundingBoxBase<PointClass> &other) const {
|
||||
bool contains(const BoundingBoxBase<PointType, PointsType> &other) const {
|
||||
return contains(other.min) && contains(other.max);
|
||||
}
|
||||
bool overlap(const BoundingBoxBase<PointClass> &other) const {
|
||||
bool overlap(const BoundingBoxBase<PointType, PointsType> &other) const {
|
||||
return ! (this->max.x() < other.min.x() || this->min.x() > other.max.x() ||
|
||||
this->max.y() < other.min.y() || this->min.y() > other.max.y());
|
||||
}
|
||||
bool operator==(const BoundingBoxBase<PointClass> &rhs) { return this->min == rhs.min && this->max == rhs.max; }
|
||||
bool operator!=(const BoundingBoxBase<PointClass> &rhs) { return ! (*this == rhs); }
|
||||
bool operator==(const BoundingBoxBase<PointType, PointsType> &rhs) { return this->min == rhs.min && this->max == rhs.max; }
|
||||
bool operator!=(const BoundingBoxBase<PointType, PointsType> &rhs) { return ! (*this == rhs); }
|
||||
|
||||
private:
|
||||
// to access construct()
|
||||
@ -69,10 +70,10 @@ private:
|
||||
{
|
||||
if (from != to) {
|
||||
auto it = from;
|
||||
out.min = it->template cast<typename PointClass::Scalar>();
|
||||
out.min = it->template cast<typename PointType::Scalar>();
|
||||
out.max = out.min;
|
||||
for (++ it; it != to; ++ it) {
|
||||
auto vec = it->template cast<typename PointClass::Scalar>();
|
||||
auto vec = it->template cast<typename PointType::Scalar>();
|
||||
out.min = out.min.cwiseMin(vec);
|
||||
out.max = out.max.cwiseMax(vec);
|
||||
}
|
||||
@ -81,16 +82,18 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
template <class PointClass>
|
||||
class BoundingBox3Base : public BoundingBoxBase<PointClass>
|
||||
template <class PointType>
|
||||
class BoundingBox3Base : public BoundingBoxBase<PointType, std::vector<PointType>>
|
||||
{
|
||||
public:
|
||||
BoundingBox3Base() : BoundingBoxBase<PointClass>() {}
|
||||
BoundingBox3Base(const PointClass &pmin, const PointClass &pmax) :
|
||||
BoundingBoxBase<PointClass>(pmin, pmax)
|
||||
{ if (pmin.z() >= pmax.z()) BoundingBoxBase<PointClass>::defined = false; }
|
||||
BoundingBox3Base(const PointClass &p1, const PointClass &p2, const PointClass &p3) :
|
||||
BoundingBoxBase<PointClass>(p1, p1) { merge(p2); merge(p3); }
|
||||
using PointsType = std::vector<PointType>;
|
||||
|
||||
BoundingBox3Base() : BoundingBoxBase<PointType>() {}
|
||||
BoundingBox3Base(const PointType &pmin, const PointType &pmax) :
|
||||
BoundingBoxBase<PointType>(pmin, pmax)
|
||||
{ if (pmin.z() >= pmax.z()) BoundingBoxBase<PointType>::defined = false; }
|
||||
BoundingBox3Base(const PointType &p1, const PointType &p2, const PointType &p3) :
|
||||
BoundingBoxBase<PointType>(p1, p1) { merge(p2); merge(p3); }
|
||||
|
||||
template<class It, class = IteratorOnly<It> > BoundingBox3Base(It from, It to)
|
||||
{
|
||||
@ -98,67 +101,67 @@ public:
|
||||
throw Slic3r::InvalidArgument("Empty point set supplied to BoundingBox3Base constructor");
|
||||
|
||||
auto it = from;
|
||||
this->min = it->template cast<typename PointClass::Scalar>();
|
||||
this->min = it->template cast<typename PointType::Scalar>();
|
||||
this->max = this->min;
|
||||
for (++ it; it != to; ++ it) {
|
||||
auto vec = it->template cast<typename PointClass::Scalar>();
|
||||
auto vec = it->template cast<typename PointType::Scalar>();
|
||||
this->min = this->min.cwiseMin(vec);
|
||||
this->max = this->max.cwiseMax(vec);
|
||||
}
|
||||
this->defined = (this->min.x() < this->max.x()) && (this->min.y() < this->max.y()) && (this->min.z() < this->max.z());
|
||||
}
|
||||
|
||||
BoundingBox3Base(const std::vector<PointClass> &points)
|
||||
BoundingBox3Base(const PointsType &points)
|
||||
: BoundingBox3Base(points.begin(), points.end())
|
||||
{}
|
||||
|
||||
void merge(const PointClass &point);
|
||||
void merge(const std::vector<PointClass> &points);
|
||||
void merge(const BoundingBox3Base<PointClass> &bb);
|
||||
PointClass size() const;
|
||||
void merge(const PointType &point);
|
||||
void merge(const PointsType &points);
|
||||
void merge(const BoundingBox3Base<PointType> &bb);
|
||||
PointType size() const;
|
||||
double radius() const;
|
||||
void translate(coordf_t x, coordf_t y, coordf_t z) { assert(this->defined); PointClass v(x, y, z); this->min += v; this->max += v; }
|
||||
void translate(coordf_t x, coordf_t y, coordf_t z) { assert(this->defined); PointType v(x, y, z); this->min += v; this->max += v; }
|
||||
void translate(const Vec3d &v) { this->min += v; this->max += v; }
|
||||
void offset(coordf_t delta);
|
||||
BoundingBox3Base<PointClass> inflated(coordf_t delta) const throw() { BoundingBox3Base<PointClass> out(*this); out.offset(delta); return out; }
|
||||
PointClass center() const;
|
||||
BoundingBox3Base<PointType> inflated(coordf_t delta) const throw() { BoundingBox3Base<PointType> out(*this); out.offset(delta); return out; }
|
||||
PointType center() const;
|
||||
coordf_t max_size() const;
|
||||
|
||||
bool contains(const PointClass &point) const {
|
||||
return BoundingBoxBase<PointClass>::contains(point) && point.z() >= this->min.z() && point.z() <= this->max.z();
|
||||
bool contains(const PointType &point) const {
|
||||
return BoundingBoxBase<PointType>::contains(point) && point.z() >= this->min.z() && point.z() <= this->max.z();
|
||||
}
|
||||
|
||||
bool contains(const BoundingBox3Base<PointClass>& other) const {
|
||||
bool contains(const BoundingBox3Base<PointType>& other) const {
|
||||
return contains(other.min) && contains(other.max);
|
||||
}
|
||||
|
||||
// Intersects without boundaries.
|
||||
bool intersects(const BoundingBox3Base<PointClass>& other) const {
|
||||
bool intersects(const BoundingBox3Base<PointType>& other) const {
|
||||
return this->min.x() < other.max.x() && this->max.x() > other.min.x() && this->min.y() < other.max.y() && this->max.y() > other.min.y() &&
|
||||
this->min.z() < other.max.z() && this->max.z() > other.min.z();
|
||||
}
|
||||
};
|
||||
|
||||
// Will prevent warnings caused by non existing definition of template in hpp
|
||||
extern template void BoundingBoxBase<Point>::scale(double factor);
|
||||
extern template void BoundingBoxBase<Point, Points>::scale(double factor);
|
||||
extern template void BoundingBoxBase<Vec2d>::scale(double factor);
|
||||
extern template void BoundingBoxBase<Vec3d>::scale(double factor);
|
||||
extern template void BoundingBoxBase<Point>::offset(coordf_t delta);
|
||||
extern template void BoundingBoxBase<Point, Points>::offset(coordf_t delta);
|
||||
extern template void BoundingBoxBase<Vec2d>::offset(coordf_t delta);
|
||||
extern template void BoundingBoxBase<Point>::merge(const Point &point);
|
||||
extern template void BoundingBoxBase<Point, Points>::merge(const Point &point);
|
||||
extern template void BoundingBoxBase<Vec2f>::merge(const Vec2f &point);
|
||||
extern template void BoundingBoxBase<Vec2d>::merge(const Vec2d &point);
|
||||
extern template void BoundingBoxBase<Point>::merge(const Points &points);
|
||||
extern template void BoundingBoxBase<Point, Points>::merge(const Points &points);
|
||||
extern template void BoundingBoxBase<Vec2d>::merge(const Pointfs &points);
|
||||
extern template void BoundingBoxBase<Point>::merge(const BoundingBoxBase<Point> &bb);
|
||||
extern template void BoundingBoxBase<Point, Points>::merge(const BoundingBoxBase<Point, Points> &bb);
|
||||
extern template void BoundingBoxBase<Vec2f>::merge(const BoundingBoxBase<Vec2f> &bb);
|
||||
extern template void BoundingBoxBase<Vec2d>::merge(const BoundingBoxBase<Vec2d> &bb);
|
||||
extern template Point BoundingBoxBase<Point>::size() const;
|
||||
extern template Point BoundingBoxBase<Point, Points>::size() const;
|
||||
extern template Vec2f BoundingBoxBase<Vec2f>::size() const;
|
||||
extern template Vec2d BoundingBoxBase<Vec2d>::size() const;
|
||||
extern template double BoundingBoxBase<Point>::radius() const;
|
||||
extern template double BoundingBoxBase<Point, Points>::radius() const;
|
||||
extern template double BoundingBoxBase<Vec2d>::radius() const;
|
||||
extern template Point BoundingBoxBase<Point>::center() const;
|
||||
extern template Point BoundingBoxBase<Point, Points>::center() const;
|
||||
extern template Vec2f BoundingBoxBase<Vec2f>::center() const;
|
||||
extern template Vec2d BoundingBoxBase<Vec2d>::center() const;
|
||||
extern template void BoundingBox3Base<Vec3f>::merge(const Vec3f &point);
|
||||
@ -174,7 +177,7 @@ extern template Vec3d BoundingBox3Base<Vec3d>::center() const;
|
||||
extern template coordf_t BoundingBox3Base<Vec3f>::max_size() const;
|
||||
extern template coordf_t BoundingBox3Base<Vec3d>::max_size() const;
|
||||
|
||||
class BoundingBox : public BoundingBoxBase<Point>
|
||||
class BoundingBox : public BoundingBoxBase<Point, Points>
|
||||
{
|
||||
public:
|
||||
void polygon(Polygon* polygon) const;
|
||||
@ -187,9 +190,9 @@ public:
|
||||
// to encompass the original bounding box.
|
||||
void align_to_grid(const coord_t cell_size);
|
||||
|
||||
BoundingBox() : BoundingBoxBase<Point>() {}
|
||||
BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase<Point>(pmin, pmax) {}
|
||||
BoundingBox(const Points &points) : BoundingBoxBase<Point>(points) {}
|
||||
BoundingBox() : BoundingBoxBase<Point, Points>() {}
|
||||
BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase<Point, Points>(pmin, pmax) {}
|
||||
BoundingBox(const Points &points) : BoundingBoxBase<Point, Points>(points) {}
|
||||
|
||||
BoundingBox inflated(coordf_t delta) const throw() { BoundingBox out(*this); out.offset(delta); return out; }
|
||||
|
||||
@ -222,14 +225,14 @@ public:
|
||||
BoundingBoxf3 transformed(const Transform3d& matrix) const;
|
||||
};
|
||||
|
||||
template<typename VT>
|
||||
inline bool empty(const BoundingBoxBase<VT> &bb)
|
||||
template<typename PointType, typename PointsType>
|
||||
inline bool empty(const BoundingBoxBase<PointType, PointsType> &bb)
|
||||
{
|
||||
return ! bb.defined || bb.min.x() >= bb.max.x() || bb.min.y() >= bb.max.y();
|
||||
}
|
||||
|
||||
template<typename VT>
|
||||
inline bool empty(const BoundingBox3Base<VT> &bb)
|
||||
template<typename PointType>
|
||||
inline bool empty(const BoundingBox3Base<PointType> &bb)
|
||||
{
|
||||
return ! bb.defined || bb.min.x() >= bb.max.x() || bb.min.y() >= bb.max.y() || bb.min.z() >= bb.max.z();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -40,7 +40,7 @@ inline ZPath to_zpath(const Points &path, coord_t z)
|
||||
// Convert multiple paths to paths with a given Z coordinate.
|
||||
// If Open, then duplicate the first point of each path at its end.
|
||||
template<bool Open = false>
|
||||
inline ZPaths to_zpaths(const std::vector<Points> &paths, coord_t z)
|
||||
inline ZPaths to_zpaths(const VecOfPoints &paths, coord_t z)
|
||||
{
|
||||
ZPaths out;
|
||||
out.reserve(paths.size());
|
||||
@ -86,16 +86,16 @@ inline Points from_zpath(const ZPoints &path)
|
||||
// Convert multiple paths to paths with a given Z coordinate.
|
||||
// If Open, then duplicate the first point of each path at its end.
|
||||
template<bool Open = false>
|
||||
inline void from_zpaths(const ZPaths &paths, std::vector<Points> &out)
|
||||
inline void from_zpaths(const ZPaths &paths, VecOfPoints &out)
|
||||
{
|
||||
out.reserve(out.size() + paths.size());
|
||||
for (const ZPoints &path : paths)
|
||||
out.emplace_back(from_zpath<Open>(path));
|
||||
}
|
||||
template<bool Open = false>
|
||||
inline std::vector<Points> from_zpaths(const ZPaths &paths)
|
||||
inline VecOfPoints from_zpaths(const ZPaths &paths)
|
||||
{
|
||||
std::vector<Points> out;
|
||||
VecOfPoints out;
|
||||
from_zpaths(paths, out);
|
||||
return out;
|
||||
}
|
||||
|
@ -1991,7 +1991,7 @@ public:
|
||||
|
||||
void set_enum_labels(GUIType gui_type, const std::initializer_list<std::string_view> il) {
|
||||
this->enum_def_new();
|
||||
assert(gui_type == GUIType::i_enum_open || gui_type == GUIType::f_enum_open || gui_type == ConfigOptionDef::GUIType::select_open);
|
||||
assert(gui_type == GUIType::i_enum_open || gui_type == GUIType::f_enum_open || gui_type == ConfigOptionDef::GUIType::select_close);
|
||||
this->gui_type = gui_type;
|
||||
enum_def->set_labels(il);
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ public:
|
||||
Contour() = default;
|
||||
Contour(const Slic3r::Point *begin, const Slic3r::Point *end, bool open) : m_begin(begin), m_end(end), m_open(open) {}
|
||||
Contour(const Slic3r::Point *data, size_t size, bool open) : Contour(data, data + size, open) {}
|
||||
Contour(const std::vector<Slic3r::Point> &pts, bool open) : Contour(pts.data(), pts.size(), open) {}
|
||||
Contour(const Points &pts, bool open) : Contour(pts.data(), pts.size(), open) {}
|
||||
|
||||
const Slic3r::Point *begin() const { return m_begin; }
|
||||
const Slic3r::Point *end() const { return m_end; }
|
||||
|
@ -597,7 +597,8 @@ ExPolygon elephant_foot_compensation(const ExPolygon &input_expoly, double min_c
|
||||
}
|
||||
|
||||
ExPolygons out_vec = variable_offset_inner_ex(resampled, deltas, 2.);
|
||||
if (out_vec.size() == 1)
|
||||
if (out_vec.size() == 1 && out_vec.front().holes.size() == resampled.holes.size())
|
||||
// No contour of the original compensated expolygon was lost.
|
||||
out = std::move(out_vec.front());
|
||||
else {
|
||||
// Something went wrong, don't compensate.
|
||||
@ -610,6 +611,7 @@ ExPolygon elephant_foot_compensation(const ExPolygon &input_expoly, double min_c
|
||||
{ { out_vec }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } } });
|
||||
}
|
||||
#endif /* TESTS_EXPORT_SVGS */
|
||||
// It may be that the source expolygons contained non-manifold vertices, for which the variable offset may not produce the same number of contours or holes.
|
||||
assert(out_vec.size() == 1);
|
||||
}
|
||||
}
|
||||
|
@ -796,8 +796,7 @@ const Glyph* priv::get_glyph(
|
||||
auto glyph_item = cache.find(unicode);
|
||||
if (glyph_item != cache.end()) return &glyph_item->second;
|
||||
|
||||
unsigned int font_index = font_prop.collection_number.has_value()?
|
||||
*font_prop.collection_number : 0;
|
||||
unsigned int font_index = font_prop.collection_number.value_or(0);
|
||||
if (!is_valid(font, font_index)) return nullptr;
|
||||
|
||||
if (!font_info_opt.has_value()) {
|
||||
@ -835,11 +834,10 @@ const Glyph* priv::get_glyph(
|
||||
glyph_opt->shape = Slic3r::union_ex(offset_ex(glyph_opt->shape, delta));
|
||||
}
|
||||
if (font_prop.skew.has_value()) {
|
||||
const float &ratio = *font_prop.skew;
|
||||
auto skew = [&ratio](Polygon &polygon) {
|
||||
for (Slic3r::Point &p : polygon.points) {
|
||||
p.x() += p.y() * ratio;
|
||||
}
|
||||
double ratio = *font_prop.skew;
|
||||
auto skew = [&ratio](Polygon &polygon) {
|
||||
for (Slic3r::Point &p : polygon.points)
|
||||
p.x() += static_cast<Point::coord_type>(std::round(p.y() * ratio));
|
||||
};
|
||||
for (ExPolygon &expolygon : glyph_opt->shape) {
|
||||
skew(expolygon.contour);
|
||||
@ -1363,10 +1361,9 @@ std::string Emboss::create_range_text(const std::string &text,
|
||||
|
||||
double Emboss::get_shape_scale(const FontProp &fp, const FontFile &ff)
|
||||
{
|
||||
const auto &cn = fp.collection_number;
|
||||
unsigned int font_index = (cn.has_value()) ? *cn : 0;
|
||||
int unit_per_em = ff.infos[font_index].unit_per_em;
|
||||
double scale = fp.size_in_mm / unit_per_em;
|
||||
size_t font_index = fp.collection_number.value_or(0);
|
||||
const FontFile::Info &info = ff.infos[font_index];
|
||||
double scale = fp.size_in_mm / (double) info.unit_per_em;
|
||||
// Shape is scaled for store point coordinate as integer
|
||||
return scale * SHAPE_SCALE;
|
||||
}
|
||||
|
@ -10,6 +10,8 @@
|
||||
#include <cassert>
|
||||
#include <list>
|
||||
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
void ExPolygon::scale(double factor)
|
||||
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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>
|
||||
|
147
src/libslic3r/Format/SLAArchiveFormatRegistry.cpp
Normal file
147
src/libslic3r/Format/SLAArchiveFormatRegistry.cpp
Normal 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
|
71
src/libslic3r/Format/SLAArchiveFormatRegistry.hpp
Normal file
71
src/libslic3r/Format/SLAArchiveFormatRegistry.hpp
Normal 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
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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(),
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/LocalesUtils.hpp"
|
||||
#include "libslic3r/format.hpp"
|
||||
#include "libslic3r/I18N.hpp"
|
||||
#include "libslic3r/GCodeWriter.hpp"
|
||||
#include "GCodeProcessor.hpp"
|
||||
|
||||
@ -441,9 +442,7 @@ void GCodeProcessorResult::reset() {
|
||||
max_print_height = 0.0f;
|
||||
settings_ids.reset();
|
||||
extruders_count = 0;
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
backtrace_enabled = false;
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
extruder_colors = std::vector<std::string>();
|
||||
filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
|
||||
filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY);
|
||||
@ -461,9 +460,7 @@ void GCodeProcessorResult::reset() {
|
||||
max_print_height = 0.0f;
|
||||
settings_ids.reset();
|
||||
extruders_count = 0;
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
backtrace_enabled = false;
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
extruder_colors = std::vector<std::string>();
|
||||
filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
|
||||
filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY);
|
||||
@ -557,9 +554,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
|
||||
m_producer = EProducer::PrusaSlicer;
|
||||
m_flavor = config.gcode_flavor;
|
||||
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
m_result.backtrace_enabled = is_XL_printer(config);
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
|
||||
size_t extruders_count = config.nozzle_diameter.values.size();
|
||||
m_result.extruders_count = extruders_count;
|
||||
@ -3384,7 +3379,7 @@ void GCodeProcessor::process_T(const std::string_view command)
|
||||
if (m_extruder_id != id) {
|
||||
if (((m_producer == EProducer::PrusaSlicer || m_producer == EProducer::Slic3rPE || m_producer == EProducer::Slic3r) && id >= m_result.extruders_count) ||
|
||||
((m_producer != EProducer::PrusaSlicer && m_producer != EProducer::Slic3rPE && m_producer != EProducer::Slic3r) && id >= m_result.extruder_colors.size()))
|
||||
BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid toolchange, maybe from a custom gcode.";
|
||||
BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid toolchange, maybe from a custom gcode (" << command << ").";
|
||||
else {
|
||||
unsigned char old_extruder_id = m_extruder_id;
|
||||
process_filaments(CustomGCode::ToolChange);
|
||||
@ -3476,7 +3471,6 @@ void GCodeProcessor::post_process()
|
||||
last_exported_stop[i] = time_in_minutes(m_time_processor.machines[i].time);
|
||||
}
|
||||
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
// Helper class to modify and export gcode to file
|
||||
class ExportLines
|
||||
{
|
||||
@ -3566,7 +3560,7 @@ void GCodeProcessor::post_process()
|
||||
++m_curr_g1_id;
|
||||
}
|
||||
|
||||
if (it != init_it || m_curr_g1_id == 0)
|
||||
if ((it != m_machine.g1_times_cache.end() && it != init_it) || m_curr_g1_id == 0)
|
||||
m_time = it->elapsed_time;
|
||||
}
|
||||
|
||||
@ -3610,7 +3604,7 @@ void GCodeProcessor::post_process()
|
||||
last_time_insertion = rev_it->time;
|
||||
const std::string out_line = line_inserter(i + 1, last_time_insertion, m_time - last_time_insertion);
|
||||
rev_it_dist = std::distance(m_lines.rbegin(), rev_it) + 1;
|
||||
const auto new_it = m_lines.insert(rev_it.base(), { out_line, rev_it->time });
|
||||
m_lines.insert(rev_it.base(), { out_line, rev_it->time });
|
||||
#ifndef NDEBUG
|
||||
m_statistics.add_line(out_line.length());
|
||||
#endif // NDEBUG
|
||||
@ -3711,26 +3705,15 @@ void GCodeProcessor::post_process()
|
||||
};
|
||||
|
||||
ExportLines export_lines(m_result.backtrace_enabled ? ExportLines::EWriteType::ByTime : ExportLines::EWriteType::BySize, m_time_processor.machines[0]);
|
||||
#else
|
||||
// buffer line to export only when greater than 64K to reduce writing calls
|
||||
std::string export_line;
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
|
||||
// replace placeholder lines with the proper final value
|
||||
// gcode_line is in/out parameter, to reduce expensive memory allocation
|
||||
auto process_placeholders = [&](std::string& gcode_line) {
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
bool processed = false;
|
||||
#else
|
||||
unsigned int extra_lines_count = 0;
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
|
||||
// remove trailing '\n'
|
||||
auto line = std::string_view(gcode_line).substr(0, gcode_line.length() - 1);
|
||||
|
||||
#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
std::string ret;
|
||||
#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
if (line.length() > 1) {
|
||||
line = line.substr(1);
|
||||
if (m_time_processor.export_remaining_time_enabled &&
|
||||
@ -3739,29 +3722,16 @@ void GCodeProcessor::post_process()
|
||||
const TimeMachine& machine = m_time_processor.machines[i];
|
||||
if (machine.enabled) {
|
||||
// export pair <percent, remaining time>
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
export_lines.append_line(format_line_M73_main(machine.line_m73_main_mask.c_str(),
|
||||
(line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? 0 : 100,
|
||||
(line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? time_in_minutes(machine.time) : 0));
|
||||
processed = true;
|
||||
#else
|
||||
ret += format_line_M73_main(machine.line_m73_main_mask.c_str(),
|
||||
(line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? 0 : 100,
|
||||
(line == reserved_tag(ETags::First_Line_M73_Placeholder)) ? time_in_minutes(machine.time) : 0);
|
||||
++extra_lines_count;
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
|
||||
// export remaining time to next printer stop
|
||||
if (line == reserved_tag(ETags::First_Line_M73_Placeholder) && !machine.stop_times.empty()) {
|
||||
const int to_export_stop = time_in_minutes(machine.stop_times.front().elapsed_time);
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
export_lines.append_line(format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop));
|
||||
last_exported_stop[i] = to_export_stop;
|
||||
#else
|
||||
ret += format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop);
|
||||
last_exported_stop[i] = to_export_stop;
|
||||
++extra_lines_count;
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3775,12 +3745,8 @@ void GCodeProcessor::post_process()
|
||||
sprintf(buf, "; estimated printing time (%s mode) = %s\n",
|
||||
(mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent",
|
||||
get_time_dhms(machine.time).c_str());
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
export_lines.append_line(buf);
|
||||
processed = true;
|
||||
#else
|
||||
ret += buf;
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||
@ -3791,25 +3757,14 @@ void GCodeProcessor::post_process()
|
||||
sprintf(buf, "; estimated first layer printing time (%s mode) = %s\n",
|
||||
(mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent",
|
||||
get_time_dhms(machine.layers_time.empty() ? 0.f : machine.layers_time.front()).c_str());
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
export_lines.append_line(buf);
|
||||
processed = true;
|
||||
#else
|
||||
ret += buf;
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
return processed;
|
||||
#else
|
||||
if (!ret.empty())
|
||||
// Not moving the move operator on purpose, so that the gcode_line allocation will grow and it will not be reallocated after handful of lines are processed.
|
||||
gcode_line = ret;
|
||||
return std::tuple(!ret.empty(), (extra_lines_count == 0) ? extra_lines_count : extra_lines_count - 1);
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
};
|
||||
|
||||
std::vector<double> filament_mm(m_result.extruders_count, 0.0);
|
||||
@ -3883,16 +3838,8 @@ void GCodeProcessor::post_process()
|
||||
time_in_minutes, format_time_float, format_line_M73_main, format_line_M73_stop_int, format_line_M73_stop_float, time_in_last_minute,
|
||||
// Caches, to be modified
|
||||
&g1_times_cache_it, &last_exported_main, &last_exported_stop,
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
&export_lines]
|
||||
#else
|
||||
// String output
|
||||
&export_line]
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
(const size_t g1_lines_counter) {
|
||||
#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
unsigned int exported_lines_count = 0;
|
||||
#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
if (m_time_processor.export_remaining_time_enabled) {
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||
const TimeMachine& machine = m_time_processor.machines[i];
|
||||
@ -3906,17 +3853,9 @@ void GCodeProcessor::post_process()
|
||||
std::pair<int, int> to_export_main = { int(100.0f * it->elapsed_time / machine.time),
|
||||
time_in_minutes(machine.time - it->elapsed_time) };
|
||||
if (last_exported_main[i] != to_export_main) {
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
export_lines.append_line(format_line_M73_main(machine.line_m73_main_mask.c_str(),
|
||||
to_export_main.first, to_export_main.second));
|
||||
#else
|
||||
export_line += format_line_M73_main(machine.line_m73_main_mask.c_str(),
|
||||
to_export_main.first, to_export_main.second);
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
last_exported_main[i] = to_export_main;
|
||||
#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
++exported_lines_count;
|
||||
#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
}
|
||||
// export remaining time to next printer stop
|
||||
auto it_stop = std::upper_bound(machine.stop_times.begin(), machine.stop_times.end(), it->elapsed_time,
|
||||
@ -3926,15 +3865,8 @@ void GCodeProcessor::post_process()
|
||||
if (last_exported_stop[i] != to_export_stop) {
|
||||
if (to_export_stop > 0) {
|
||||
if (last_exported_stop[i] != to_export_stop) {
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
export_lines.append_line(format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop));
|
||||
#else
|
||||
export_line += format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop);
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
last_exported_stop[i] = to_export_stop;
|
||||
#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
++exported_lines_count;
|
||||
#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -3953,22 +3885,12 @@ void GCodeProcessor::post_process()
|
||||
}
|
||||
|
||||
if (is_last) {
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
if (std::distance(machine.stop_times.begin(), it_stop) == static_cast<ptrdiff_t>(machine.stop_times.size() - 1))
|
||||
export_lines.append_line(format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop));
|
||||
else
|
||||
export_lines.append_line(format_line_M73_stop_float(machine.line_m73_stop_mask.c_str(), time_in_last_minute(it_stop->elapsed_time - it->elapsed_time)));
|
||||
#else
|
||||
if (std::distance(machine.stop_times.begin(), it_stop) == static_cast<ptrdiff_t>(machine.stop_times.size() - 1))
|
||||
export_line += format_line_M73_stop_int(machine.line_m73_stop_mask.c_str(), to_export_stop);
|
||||
else
|
||||
export_line += format_line_M73_stop_float(machine.line_m73_stop_mask.c_str(), time_in_last_minute(it_stop->elapsed_time - it->elapsed_time));
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
|
||||
last_exported_stop[i] = to_export_stop;
|
||||
#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
++exported_lines_count;
|
||||
#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3977,12 +3899,8 @@ void GCodeProcessor::post_process()
|
||||
}
|
||||
}
|
||||
}
|
||||
#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
return exported_lines_count;
|
||||
#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
};
|
||||
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
// add lines XXX to exported gcode
|
||||
auto process_line_T = [this, &export_lines](const std::string& gcode_line, const size_t g1_lines_counter, const ExportLines::Backtrace& backtrace) {
|
||||
const std::string cmd = GCodeReader::GCodeLine::extract_cmd(gcode_line);
|
||||
@ -3991,6 +3909,18 @@ void GCodeProcessor::post_process()
|
||||
int tool_number = -1;
|
||||
ss >> tool_number;
|
||||
if (tool_number != -1)
|
||||
if (tool_number < 0 || (int)m_extruder_temps_config.size() <= tool_number) {
|
||||
// found an invalid value, clamp it to a valid one
|
||||
tool_number = std::clamp<int>(0, m_extruder_temps_config.size() - 1, tool_number);
|
||||
// emit warning
|
||||
std::string warning = _u8L("GCode Post-Processor encountered an invalid toolchange, maybe from a custom gcode:");
|
||||
warning += "\n> ";
|
||||
warning += gcode_line;
|
||||
warning += _u8L("Generated M104 lines may be incorrect.");
|
||||
BOOST_LOG_TRIVIAL(error) << warning;
|
||||
if (m_print != nullptr)
|
||||
m_print->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, warning);
|
||||
}
|
||||
export_lines.insert_lines(backtrace, cmd,
|
||||
// line inserter
|
||||
[tool_number, this](unsigned int id, float time, float time_diff) {
|
||||
@ -4015,37 +3945,15 @@ void GCodeProcessor::post_process()
|
||||
});
|
||||
}
|
||||
};
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
|
||||
m_result.lines_ends.clear();
|
||||
#if !ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
// helper function to write to disk
|
||||
size_t out_file_pos = 0;
|
||||
auto write_string = [this, &export_line, &out, &out_path, &out_file_pos](const std::string& str) {
|
||||
fwrite((const void*)export_line.c_str(), 1, export_line.length(), out.f);
|
||||
if (ferror(out.f)) {
|
||||
out.close();
|
||||
boost::nowide::remove(out_path.c_str());
|
||||
throw Slic3r::RuntimeError(std::string("GCode processor post process export failed.\nIs the disk full?\n"));
|
||||
}
|
||||
for (size_t i = 0; i < export_line.size(); ++i)
|
||||
if (export_line[i] == '\n')
|
||||
m_result.lines_ends.emplace_back(out_file_pos + i + 1);
|
||||
out_file_pos += export_line.size();
|
||||
export_line.clear();
|
||||
};
|
||||
#endif // !ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
|
||||
unsigned int line_id = 0;
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
// Backtrace data for Tx gcode lines
|
||||
static const ExportLines::Backtrace backtrace_T = { 120.0f, 10 };
|
||||
// In case there are multiple sources of backtracing, keeps track of the longest backtrack time needed
|
||||
// to flush the backtrace cache accordingly
|
||||
float max_backtrace_time = 120.0f;
|
||||
#else
|
||||
std::vector<std::pair<unsigned int, unsigned int>> offsets;
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
|
||||
{
|
||||
// Read the input stream 64kB at a time, extract lines and process them.
|
||||
@ -4069,24 +3977,15 @@ void GCodeProcessor::post_process()
|
||||
gcode_line.insert(gcode_line.end(), it, it_end);
|
||||
if (eol) {
|
||||
++line_id;
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
export_lines.update(line_id, g1_lines_counter);
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
|
||||
gcode_line += "\n";
|
||||
// replace placeholder lines
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
bool processed = process_placeholders(gcode_line);
|
||||
if (processed)
|
||||
gcode_line.clear();
|
||||
#else
|
||||
auto [processed, lines_added_count] = process_placeholders(gcode_line);
|
||||
if (processed && lines_added_count > 0)
|
||||
offsets.push_back({ line_id, lines_added_count });
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
if (!processed)
|
||||
processed = process_used_filament(gcode_line);
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
if (!processed && !is_temporary_decoration(gcode_line)) {
|
||||
if (GCodeReader::GCodeLine::cmd_is(gcode_line, "G1"))
|
||||
// add lines M73 where needed
|
||||
@ -4101,18 +4000,6 @@ void GCodeProcessor::post_process()
|
||||
if (!gcode_line.empty())
|
||||
export_lines.append_line(gcode_line);
|
||||
export_lines.write(out, 1.1f * max_backtrace_time, m_result, out_path);
|
||||
#else
|
||||
if (!processed && !is_temporary_decoration(gcode_line) && GCodeReader::GCodeLine::cmd_is(gcode_line, "G1")) {
|
||||
// remove temporary lines, add lines M73 where needed
|
||||
unsigned int extra_lines_count = process_line_G1(g1_lines_counter++);
|
||||
if (extra_lines_count > 0)
|
||||
offsets.push_back({ line_id, extra_lines_count });
|
||||
}
|
||||
|
||||
export_line += gcode_line;
|
||||
if (export_line.length() > 65535)
|
||||
write_string(export_line);
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
gcode_line.clear();
|
||||
}
|
||||
// Skip EOL.
|
||||
@ -4127,30 +4014,12 @@ void GCodeProcessor::post_process()
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
export_lines.flush(out, m_result, out_path);
|
||||
#else
|
||||
if (!export_line.empty())
|
||||
write_string(export_line);
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
|
||||
out.close();
|
||||
in.close();
|
||||
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
export_lines.synchronize_moves(m_result);
|
||||
#else
|
||||
// updates moves' gcode ids which have been modified by the insertion of the M73 lines
|
||||
unsigned int curr_offset_id = 0;
|
||||
unsigned int total_offset = 0;
|
||||
for (GCodeProcessorResult::MoveVertex& move : m_result.moves) {
|
||||
while (curr_offset_id < static_cast<unsigned int>(offsets.size()) && offsets[curr_offset_id].first <= move.gcode_id) {
|
||||
total_offset += offsets[curr_offset_id].second;
|
||||
++curr_offset_id;
|
||||
}
|
||||
move.gcode_id += total_offset;
|
||||
}
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
|
||||
if (rename_file(out_path, m_result.filename))
|
||||
throw Slic3r::RuntimeError(std::string("Failed to rename the output G-code file from ") + out_path + " to " + m_result.filename + '\n' +
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Print;
|
||||
|
||||
enum class EMoveType : unsigned char
|
||||
{
|
||||
Noop,
|
||||
@ -125,9 +127,7 @@ namespace Slic3r {
|
||||
float max_print_height;
|
||||
SettingsIds settings_ids;
|
||||
size_t extruders_count;
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
bool backtrace_enabled;
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
std::vector<std::string> extruder_colors;
|
||||
std::vector<float> filament_diameters;
|
||||
std::vector<float> filament_densities;
|
||||
@ -588,6 +588,8 @@ namespace Slic3r {
|
||||
TimeProcessor m_time_processor;
|
||||
UsedFilaments m_used_filaments;
|
||||
|
||||
Print* m_print{ nullptr };
|
||||
|
||||
GCodeProcessorResult m_result;
|
||||
static unsigned int s_result_id;
|
||||
|
||||
@ -601,6 +603,8 @@ namespace Slic3r {
|
||||
GCodeProcessor();
|
||||
|
||||
void apply_config(const PrintConfig& config);
|
||||
void set_print(Print* print) { m_print = print; }
|
||||
|
||||
void enable_stealth_time_estimator(bool enabled);
|
||||
bool is_stealth_time_estimator_enabled() const {
|
||||
return m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].enabled;
|
||||
|
@ -437,7 +437,7 @@ Polygons extract_perimeter_polygons(const Layer *layer, std::vector<const LayerR
|
||||
|
||||
if (polygons.empty()) { // If there are no perimeter polygons for whatever reason (disabled perimeters .. ) insert dummy point
|
||||
// it is easier than checking everywhere if the layer is not emtpy, no seam will be placed to this layer anyway
|
||||
polygons.emplace_back(std::vector { Point { 0, 0 } });
|
||||
polygons.emplace_back(Points{ { 0, 0 } });
|
||||
corresponding_regions_out.push_back(nullptr);
|
||||
}
|
||||
|
||||
|
@ -71,7 +71,6 @@ public:
|
||||
return strncmp(cmd, cmd_test, len) == 0 && GCodeReader::is_end_of_word(cmd[len]);
|
||||
}
|
||||
|
||||
#if ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
static bool cmd_starts_with(const std::string& gcode_line, const char* cmd_test) {
|
||||
return strncmp(GCodeReader::skip_whitespaces(gcode_line.c_str()), cmd_test, strlen(cmd_test)) == 0;
|
||||
}
|
||||
@ -82,7 +81,6 @@ public:
|
||||
const std::string_view cmd = temp.cmd();
|
||||
return { cmd.begin(), cmd.end() };
|
||||
}
|
||||
#endif // ENABLE_GCODE_POSTPROCESS_BACKTRACE
|
||||
|
||||
private:
|
||||
std::string m_raw;
|
||||
|
@ -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)
|
||||
|
@ -12,11 +12,6 @@
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace ClipperLib {
|
||||
class PolyNode;
|
||||
using PolyNodes = std::vector<PolyNode*>;
|
||||
}
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
// Generic result of an orientation predicate.
|
||||
|
@ -19,6 +19,8 @@
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <oneapi/tbb/scalable_allocator.h>
|
||||
|
||||
//#define DEBUG_FILES
|
||||
#ifdef DEBUG_FILES
|
||||
#include "libslic3r/SVG.hpp"
|
||||
@ -211,8 +213,8 @@ void JPSPathFinder::add_obstacles(const Layer *layer, const Point &global_origin
|
||||
|
||||
this->print_z = layer->print_z;
|
||||
Lines obstacles;
|
||||
obstacles.reserve(layer->malformed_lines.size());
|
||||
for (const Line &l : layer->malformed_lines) { obstacles.push_back(Line{l.a + global_origin, l.b + global_origin}); }
|
||||
obstacles.reserve(layer->curled_lines.size());
|
||||
for (const Line &l : layer->curled_lines) { obstacles.push_back(Line{l.a + global_origin, l.b + global_origin}); }
|
||||
add_obstacles(obstacles);
|
||||
}
|
||||
|
||||
@ -267,7 +269,7 @@ Polyline JPSPathFinder::find_path(const Point &p0, const Point &p1)
|
||||
using QNode = astar::QNode<JPSTracer<Pixel, decltype(cell_query)>>;
|
||||
|
||||
std::unordered_map<size_t, QNode> astar_cache{};
|
||||
std::vector<Pixel> out_path;
|
||||
std::vector<Pixel, PointsAllocator<Pixel>> out_path;
|
||||
std::vector<decltype(tracer)::Node> out_nodes;
|
||||
|
||||
if (!astar::search_route(tracer, {start, {0, 0}}, std::back_inserter(out_nodes), astar_cache)) {
|
||||
@ -306,7 +308,7 @@ Polyline JPSPathFinder::find_path(const Point &p0, const Point &p1)
|
||||
svg.draw(scaled_point(start), "green", scale_(0.4));
|
||||
#endif
|
||||
|
||||
std::vector<Pixel> tmp_path;
|
||||
std::vector<Pixel, PointsAllocator<Pixel>> tmp_path;
|
||||
tmp_path.reserve(out_path.size());
|
||||
// Some path found, reverse and remove points that do not change direction
|
||||
std::reverse(out_path.begin(), out_path.end());
|
||||
|
@ -60,7 +60,10 @@ void Layer::make_slices()
|
||||
}
|
||||
|
||||
// used by Layer::build_up_down_graph()
|
||||
[[nodiscard]] static ClipperLib_Z::Paths expolygons_to_zpaths(const ExPolygons &expolygons, coord_t isrc)
|
||||
// Shrink source polygons one by one, so that they will be separated if they were touching
|
||||
// at vertices (non-manifold situation).
|
||||
// Then convert them to Z-paths with Z coordinate indicating index of the source expolygon.
|
||||
[[nodiscard]] static ClipperLib_Z::Paths expolygons_to_zpaths_shrunk(const ExPolygons &expolygons, coord_t isrc)
|
||||
{
|
||||
size_t num_paths = 0;
|
||||
for (const ExPolygon &expolygon : expolygons)
|
||||
@ -69,15 +72,89 @@ void Layer::make_slices()
|
||||
ClipperLib_Z::Paths out;
|
||||
out.reserve(num_paths);
|
||||
|
||||
for (const ExPolygon &expolygon : expolygons) {
|
||||
for (size_t icontour = 0; icontour < expolygon.num_contours(); ++ icontour) {
|
||||
const Polygon &contour = expolygon.contour_or_hole(icontour);
|
||||
out.emplace_back();
|
||||
ClipperLib_Z::Path &path = out.back();
|
||||
path.reserve(contour.size());
|
||||
for (const Point &p : contour.points)
|
||||
path.push_back({ p.x(), p.y(), isrc });
|
||||
ClipperLib::Paths contours;
|
||||
ClipperLib::Paths holes;
|
||||
ClipperLib::Clipper clipper;
|
||||
ClipperLib::ClipperOffset co;
|
||||
ClipperLib::Paths out2;
|
||||
|
||||
// Top / bottom surfaces must overlap more than 2um to be chained into a Z graph.
|
||||
// Also a larger offset will likely be more robust on non-manifold input polygons.
|
||||
static constexpr const float delta = scaled<float>(0.001);
|
||||
co.MiterLimit = scaled<double>(3.);
|
||||
// Use the default zero edge merging distance. For this kind of safety offset the accuracy of normal direction is not important.
|
||||
// co.ShortestEdgeLength = delta * ClipperOffsetShortestEdgeFactor;
|
||||
static constexpr const double accept_area_threshold_ccw = sqr(scaled<double>(0.1 * delta));
|
||||
// Such a small hole should not survive the shrinkage, it should grow over
|
||||
static constexpr const double accept_area_threshold_cw = sqr(scaled<double>(0.2 * delta));
|
||||
|
||||
for (const ExPolygon &expoly : expolygons) {
|
||||
contours.clear();
|
||||
co.Clear();
|
||||
co.AddPath(expoly.contour.points, ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
|
||||
co.Execute(contours, - delta);
|
||||
size_t num_prev = out.size();
|
||||
if (! contours.empty()) {
|
||||
holes.clear();
|
||||
for (const Polygon &hole : expoly.holes) {
|
||||
co.Clear();
|
||||
co.AddPath(hole.points, ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
|
||||
// Execute reorients the contours so that the outer most contour has a positive area. Thus the output
|
||||
// contours will be CCW oriented even though the input paths are CW oriented.
|
||||
// Offset is applied after contour reorientation, thus the signum of the offset value is reversed.
|
||||
out2.clear();
|
||||
co.Execute(out2, delta);
|
||||
append(holes, std::move(out2));
|
||||
}
|
||||
// Subtract holes from the contours.
|
||||
if (! holes.empty()) {
|
||||
clipper.Clear();
|
||||
clipper.AddPaths(contours, ClipperLib::ptSubject, true);
|
||||
clipper.AddPaths(holes, ClipperLib::ptClip, true);
|
||||
contours.clear();
|
||||
clipper.Execute(ClipperLib::ctDifference, contours, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
|
||||
}
|
||||
for (const auto &contour : contours) {
|
||||
bool accept = true;
|
||||
// Trying to get rid of offset artifacts, that may be created due to numerical issues in offsetting algorithm
|
||||
// or due to self-intersections in the source polygons.
|
||||
//FIXME how reliable is it? Is it helpful or harmful? It seems to do more harm than good as it tends to punch holes
|
||||
// into existing ExPolygons.
|
||||
#if 0
|
||||
if (contour.size() < 8) {
|
||||
// Only accept contours with area bigger than some threshold.
|
||||
double a = ClipperLib::Area(contour);
|
||||
// Polygon has to be bigger than some threshold to be accepted.
|
||||
// Hole to be accepted has to have an area slightly bigger than the non-hole, so it will not happen due to rounding errors,
|
||||
// that a hole will be accepted without its outer contour.
|
||||
accept = a > 0 ? a > accept_area_threshold_ccw : a < - accept_area_threshold_cw;
|
||||
}
|
||||
#endif
|
||||
if (accept) {
|
||||
out.emplace_back();
|
||||
ClipperLib_Z::Path &path = out.back();
|
||||
path.reserve(contour.size());
|
||||
for (const Point &p : contour)
|
||||
path.push_back({ p.x(), p.y(), isrc });
|
||||
}
|
||||
}
|
||||
}
|
||||
#if 0 // #ifndef NDEBUG
|
||||
// Test whether the expolygons in a single layer overlap.
|
||||
Polygons test;
|
||||
for (size_t i = num_prev; i < out.size(); ++ i)
|
||||
test.emplace_back(ClipperZUtils::from_zpath(out[i]));
|
||||
Polygons outside = diff(test, to_polygons(expoly));
|
||||
if (! outside.empty()) {
|
||||
BoundingBox bbox(get_extents(expoly));
|
||||
bbox.merge(get_extents(test));
|
||||
SVG svg(debug_out_path("expolygons_to_zpaths_shrunk-self-intersections.svg").c_str(), bbox);
|
||||
svg.draw(expoly, "blue");
|
||||
svg.draw(test, "green");
|
||||
svg.draw(outside, "red");
|
||||
}
|
||||
assert(outside.empty());
|
||||
#endif // NDEBUG
|
||||
++ isrc;
|
||||
}
|
||||
|
||||
@ -126,120 +203,36 @@ static void connect_layer_slices(
|
||||
if (polynode.Contour.size() >= 3) {
|
||||
// If there is an intersection point, it should indicate which contours (one from layer below, the other from layer above) intersect.
|
||||
// Otherwise the contour is fully inside another contour.
|
||||
int32_t i = -1, j = -1;
|
||||
for (int icontour = 0; icontour <= polynode.ChildCount(); ++ icontour) {
|
||||
const ClipperLib_Z::Path &contour = icontour == 0 ? polynode.Contour : polynode.Childs[icontour - 1]->Contour;
|
||||
if (contour.size() >= 3) {
|
||||
for (const ClipperLib_Z::IntPoint &pt : contour) {
|
||||
j = pt.z();
|
||||
if (j < 0) {
|
||||
const auto &intersection = m_intersections[-j - 1];
|
||||
assert(intersection.first <= intersection.second);
|
||||
if (intersection.second < m_offset_above) {
|
||||
// Ignore intersection of polygons on the 1st layer.
|
||||
assert(intersection.first >= m_offset_below);
|
||||
j = i;
|
||||
} else if (intersection.first >= m_offset_above) {
|
||||
// Ignore intersection of polygons on the 2nd layer
|
||||
assert(intersection.second < m_offset_end);
|
||||
j = i;
|
||||
} else {
|
||||
std::tie(i, j) = m_intersections[-j - 1];
|
||||
assert(assert_intersection_valid(i, j));
|
||||
goto end;
|
||||
}
|
||||
} else if (i == -1) {
|
||||
// First source contour of this expolygon was found.
|
||||
i = j;
|
||||
} else if (i != j) {
|
||||
// Second source contour of this expolygon was found.
|
||||
if (i > j)
|
||||
std::swap(i, j);
|
||||
assert(assert_intersection_valid(i, j));
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
auto [i, j] = this->find_top_bottom_contour_ids_strict(polynode);
|
||||
bool found = false;
|
||||
if (i < 0 && j < 0) {
|
||||
// This should not happen. It may only happen if the source contours had just self intersections or intersections with contours at the same layer.
|
||||
// We may safely ignore such cases where the intersection area is meager.
|
||||
double a = ClipperLib_Z::Area(polynode.Contour);
|
||||
if (a < sqr(scaled<double>(0.001))) {
|
||||
// Ignore tiny overlaps. They are not worth resolving.
|
||||
} else {
|
||||
// We should not ignore large cases. Try to resolve the conflict by a majority of references.
|
||||
std::tie(i, j) = this->find_top_bottom_contour_ids_approx(polynode);
|
||||
// At least top or bottom should be resolved.
|
||||
assert(i >= 0 || j >= 0);
|
||||
}
|
||||
}
|
||||
end:
|
||||
bool found = false;
|
||||
if (i == -1) {
|
||||
// This should not happen. It may only happen if the source contours had just self intersections or intersections with contours at the same layer.
|
||||
assert(false);
|
||||
} else if (i == j) {
|
||||
// The contour is completely inside another contour.
|
||||
Point pt(polynode.Contour.front().x(), polynode.Contour.front().y());
|
||||
if (i < m_offset_above) {
|
||||
// Index of an island below. Look-it up in the island above.
|
||||
assert(i >= m_offset_below);
|
||||
i -= m_offset_below;
|
||||
for (int l = int(m_above.lslices_ex.size()) - 1; l >= 0; -- l) {
|
||||
LayerSlice &lslice = m_above.lslices_ex[l];
|
||||
if (lslice.bbox.contains(pt) && m_above.lslices[l].contains(pt)) {
|
||||
found = true;
|
||||
j = l;
|
||||
assert(i >= 0 && i < m_below.lslices_ex.size());
|
||||
assert(j >= 0 && j < m_above.lslices_ex.size());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
// The check above might sometimes fail when the polygons overlap only on points, which causes the clipper to detect no intersection.
|
||||
// The problem happens rarely, mostly on simple polygons (in terms of number of points), but regardless of size!
|
||||
// example of failing link on two layers, each with single polygon without holes.
|
||||
// layer A = Polygon{(-24931238,-11153865),(-22504249,-8726874),(-22504249,11477151),(-23261469,12235585),(-23752371,12727276),(-25002495,12727276),(-27502745,10227026),(-27502745,-12727274),(-26504645,-12727274)}
|
||||
// layer B = Polygon{(-24877897,-11100524),(-22504249,-8726874),(-22504249,11477151),(-23244827,12218916),(-23752371,12727276),(-25002495,12727276),(-27502745,10227026),(-27502745,-12727274),(-26504645,-12727274)}
|
||||
// note that first point is not identical, and the check above picks (-24877897,-11100524) as the first contour point (polynode.Contour.front()).
|
||||
// that point is sadly slightly outisde of the layer A, so no link is detected, eventhough they are overlaping "completely"
|
||||
Polygon contour_poly;
|
||||
for (const auto& p : polynode.Contour) {
|
||||
contour_poly.points.emplace_back(p.x(), p.y());
|
||||
}
|
||||
BoundingBox contour_aabb{contour_poly.points};
|
||||
for (int l = int(m_above.lslices_ex.size()) - 1; l >= 0; --l) {
|
||||
LayerSlice &lslice = m_above.lslices_ex[l];
|
||||
// it is potentially slow, but should be executed rarely
|
||||
if (contour_aabb.overlap(lslice.bbox) && !intersection(Polygons{contour_poly}, m_above.lslices[l]).empty()) {
|
||||
found = true;
|
||||
j = l;
|
||||
assert(i >= 0 && i < m_below.lslices_ex.size());
|
||||
assert(j >= 0 && j < m_above.lslices_ex.size());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (j < 0) {
|
||||
if (i < 0) {
|
||||
// this->find_top_bottom_contour_ids_approx() shoudl have made sure this does not happen.
|
||||
assert(false);
|
||||
} else {
|
||||
// Index of an island above. Look-it up in the island below.
|
||||
assert(j < m_offset_end);
|
||||
j -= m_offset_above;
|
||||
for (int l = int(m_below.lslices_ex.size()) - 1; l >= 0; -- l) {
|
||||
LayerSlice &lslice = m_below.lslices_ex[l];
|
||||
if (lslice.bbox.contains(pt) && m_below.lslices[l].contains(pt)) {
|
||||
found = true;
|
||||
i = l;
|
||||
assert(i >= 0 && i < m_below.lslices_ex.size());
|
||||
assert(j >= 0 && j < m_above.lslices_ex.size());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) { // Explanation for aditional check is above.
|
||||
Polygon contour_poly;
|
||||
for (const auto &p : polynode.Contour) {
|
||||
contour_poly.points.emplace_back(p.x(), p.y());
|
||||
}
|
||||
BoundingBox contour_aabb{contour_poly.points};
|
||||
for (int l = int(m_below.lslices_ex.size()) - 1; l >= 0; --l) {
|
||||
LayerSlice &lslice = m_below.lslices_ex[l];
|
||||
if (contour_aabb.overlap(lslice.bbox) && !intersection(Polygons{contour_poly}, m_below.lslices[l]).empty()) {
|
||||
found = true;
|
||||
i = l;
|
||||
assert(i >= 0 && i < m_below.lslices_ex.size());
|
||||
assert(j >= 0 && j < m_above.lslices_ex.size());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(i >= m_offset_below && i < m_offset_above);
|
||||
i -= m_offset_below;
|
||||
j = this->find_other_contour_costly(polynode, m_above, j == -2);
|
||||
found = j >= 0;
|
||||
}
|
||||
} else if (i < 0) {
|
||||
assert(j >= m_offset_above && j < m_offset_end);
|
||||
j -= m_offset_above;
|
||||
i = this->find_other_contour_costly(polynode, m_below, i == -2);
|
||||
found = i >= 0;
|
||||
} else {
|
||||
assert(assert_intersection_valid(i, j));
|
||||
i -= m_offset_below;
|
||||
@ -249,6 +242,8 @@ static void connect_layer_slices(
|
||||
found = true;
|
||||
}
|
||||
if (found) {
|
||||
assert(i >= 0 && i < m_below.lslices_ex.size());
|
||||
assert(j >= 0 && j < m_above.lslices_ex.size());
|
||||
// Subtract area of holes from the area of outer contour.
|
||||
double area = ClipperLib_Z::Area(polynode.Contour);
|
||||
for (int icontour = 0; icontour < polynode.ChildCount(); ++ icontour)
|
||||
@ -288,6 +283,187 @@ static void connect_layer_slices(
|
||||
}
|
||||
|
||||
private:
|
||||
// Find the indices of the contour below & above for an expolygon created as an intersection of two expolygons, one below, the other above.
|
||||
// Returns -1 if there is no point on the intersection refering bottom resp. top source expolygon.
|
||||
// Returns -2 if the intersection refers to multiple source expolygons on bottom resp. top layers.
|
||||
std::pair<int32_t, int32_t> find_top_bottom_contour_ids_strict(const ClipperLib_Z::PolyNode &polynode) const
|
||||
{
|
||||
// If there is an intersection point, it should indicate which contours (one from layer below, the other from layer above) intersect.
|
||||
// Otherwise the contour is fully inside another contour.
|
||||
int32_t i = -1, j = -1;
|
||||
auto process_i = [&i, &j](coord_t k) {
|
||||
if (i == -1)
|
||||
i = k;
|
||||
else if (i >= 0) {
|
||||
if (i != k) {
|
||||
// Error: Intersection contour contains points of two or more source bottom contours.
|
||||
i = -2;
|
||||
if (j == -2)
|
||||
// break
|
||||
return true;
|
||||
}
|
||||
} else
|
||||
assert(i == -2);
|
||||
return false;
|
||||
};
|
||||
auto process_j = [&i, &j](coord_t k) {
|
||||
if (j == -1)
|
||||
j = k;
|
||||
else if (j >= 0) {
|
||||
if (j != k) {
|
||||
// Error: Intersection contour contains points of two or more source top contours.
|
||||
j = -2;
|
||||
if (i == -2)
|
||||
// break
|
||||
return true;
|
||||
}
|
||||
} else
|
||||
assert(j == -2);
|
||||
return false;
|
||||
};
|
||||
for (int icontour = 0; icontour <= polynode.ChildCount(); ++ icontour) {
|
||||
const ClipperLib_Z::Path &contour = icontour == 0 ? polynode.Contour : polynode.Childs[icontour - 1]->Contour;
|
||||
if (contour.size() >= 3) {
|
||||
for (const ClipperLib_Z::IntPoint &pt : contour)
|
||||
if (coord_t k = pt.z(); k < 0) {
|
||||
const auto &intersection = m_intersections[-k - 1];
|
||||
assert(intersection.first <= intersection.second);
|
||||
if (intersection.first < m_offset_above ? process_i(intersection.first) : process_j(intersection.first))
|
||||
goto end;
|
||||
if (intersection.second < m_offset_above ? process_i(intersection.second) : process_j(intersection.second))
|
||||
goto end;
|
||||
} else if (k < m_offset_above ? process_i(k) : process_j(k))
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
end:
|
||||
return { i, j };
|
||||
}
|
||||
|
||||
// Find the indices of the contour below & above for an expolygon created as an intersection of two expolygons, one below, the other above.
|
||||
// This variant expects that the source expolygon assingment is not unique, it counts the majority.
|
||||
// Returns -1 if there is no point on the intersection refering bottom resp. top source expolygon.
|
||||
// Returns -2 if the intersection refers to multiple source expolygons on bottom resp. top layers.
|
||||
std::pair<int32_t, int32_t> find_top_bottom_contour_ids_approx(const ClipperLib_Z::PolyNode &polynode) const
|
||||
{
|
||||
// 1) Collect histogram of contour references.
|
||||
struct HistoEl {
|
||||
int32_t id;
|
||||
int32_t count;
|
||||
};
|
||||
std::vector<HistoEl> histogram;
|
||||
{
|
||||
auto increment_counter = [&histogram](const int32_t i) {
|
||||
auto it = std::lower_bound(histogram.begin(), histogram.end(), i, [](auto l, auto r){ return l.id < r; });
|
||||
if (it == histogram.end() || it->id != i)
|
||||
histogram.insert(it, HistoEl{ i, int32_t(1) });
|
||||
else
|
||||
++ it->count;
|
||||
};
|
||||
for (int icontour = 0; icontour <= polynode.ChildCount(); ++ icontour) {
|
||||
const ClipperLib_Z::Path &contour = icontour == 0 ? polynode.Contour : polynode.Childs[icontour - 1]->Contour;
|
||||
if (contour.size() >= 3) {
|
||||
for (const ClipperLib_Z::IntPoint &pt : contour)
|
||||
if (coord_t k = pt.z(); k < 0) {
|
||||
const auto &intersection = m_intersections[-k - 1];
|
||||
assert(intersection.first <= intersection.second);
|
||||
increment_counter(intersection.first);
|
||||
increment_counter(intersection.second);
|
||||
} else
|
||||
increment_counter(k);
|
||||
}
|
||||
}
|
||||
assert(! histogram.empty());
|
||||
}
|
||||
int32_t i = -1;
|
||||
int32_t j = -1;
|
||||
if (! histogram.empty()) {
|
||||
// 2) Split the histogram to bottom / top.
|
||||
auto mid = std::upper_bound(histogram.begin(), histogram.end(), m_offset_above, [](auto l, auto r){ return l < r.id; });
|
||||
// 3) Sort the bottom / top parts separately.
|
||||
auto bottom_begin = histogram.begin();
|
||||
auto bottom_end = mid;
|
||||
auto top_begin = mid;
|
||||
auto top_end = histogram.end();
|
||||
std::sort(bottom_begin, bottom_end, [](auto l, auto r) { return l.count > r.count; });
|
||||
std::sort(top_begin, top_end, [](auto l, auto r) { return l.count > r.count; });
|
||||
double i_quality = 0;
|
||||
double j_quality = 0;
|
||||
if (bottom_begin != bottom_end) {
|
||||
i = bottom_begin->id;
|
||||
i_quality = std::next(bottom_begin) == bottom_end ? std::numeric_limits<double>::max() : double(bottom_begin->count) / std::next(bottom_begin)->count;
|
||||
}
|
||||
if (top_begin != top_end) {
|
||||
j = top_begin->id;
|
||||
j_quality = std::next(top_begin) == top_end ? std::numeric_limits<double>::max() : double(top_begin->count) / std::next(top_begin)->count;
|
||||
}
|
||||
// Expected to be called only if there are duplicate references to be resolved by the histogram.
|
||||
assert(i >= 0 || j >= 0);
|
||||
assert(i_quality < std::numeric_limits<double>::max() || j_quality < std::numeric_limits<double>::max());
|
||||
if (i >= 0 && i_quality < j_quality) {
|
||||
// Force the caller to resolve the bottom references the costly but robust way.
|
||||
assert(j >= 0);
|
||||
// Twice the number of references for the best contour.
|
||||
assert(j_quality >= 2.);
|
||||
i = -2;
|
||||
} else if (j >= 0) {
|
||||
// Force the caller to resolve the top reference the costly but robust way.
|
||||
assert(i >= 0);
|
||||
// Twice the number of references for the best contour.
|
||||
assert(i_quality >= 2.);
|
||||
j = -2;
|
||||
}
|
||||
|
||||
}
|
||||
return { i, j };
|
||||
}
|
||||
|
||||
static int32_t find_other_contour_costly(const ClipperLib_Z::PolyNode &polynode, const Layer &other_layer, bool other_has_duplicates)
|
||||
{
|
||||
if (! other_has_duplicates) {
|
||||
// The contour below is likely completely inside another contour above. Look-it up in the island above.
|
||||
Point pt(polynode.Contour.front().x(), polynode.Contour.front().y());
|
||||
for (int i = int(other_layer.lslices_ex.size()) - 1; i >= 0; -- i)
|
||||
if (other_layer.lslices_ex[i].bbox.contains(pt) && other_layer.lslices[i].contains(pt))
|
||||
return i;
|
||||
// The following shall not happen now as the source expolygons are being shrunk a bit before intersecting,
|
||||
// thus each point of each intersection polygon should fit completely inside one of the original (unshrunk) expolygons.
|
||||
assert(false);
|
||||
}
|
||||
// The comment below may not be valid anymore, see the comment above. However the code is used in case the polynode contains multiple references
|
||||
// to other_layer expolygons, thus the references are not unique.
|
||||
//
|
||||
// The check above might sometimes fail when the polygons overlap only on points, which causes the clipper to detect no intersection.
|
||||
// The problem happens rarely, mostly on simple polygons (in terms of number of points), but regardless of size!
|
||||
// example of failing link on two layers, each with single polygon without holes.
|
||||
// layer A = Polygon{(-24931238,-11153865),(-22504249,-8726874),(-22504249,11477151),(-23261469,12235585),(-23752371,12727276),(-25002495,12727276),(-27502745,10227026),(-27502745,-12727274),(-26504645,-12727274)}
|
||||
// layer B = Polygon{(-24877897,-11100524),(-22504249,-8726874),(-22504249,11477151),(-23244827,12218916),(-23752371,12727276),(-25002495,12727276),(-27502745,10227026),(-27502745,-12727274),(-26504645,-12727274)}
|
||||
// note that first point is not identical, and the check above picks (-24877897,-11100524) as the first contour point (polynode.Contour.front()).
|
||||
// that point is sadly slightly outisde of the layer A, so no link is detected, eventhough they are overlaping "completely"
|
||||
Polygons contour_poly{ Polygon{ClipperZUtils::from_zpath(polynode.Contour)} };
|
||||
BoundingBox contour_aabb{contour_poly.front().points};
|
||||
int32_t i_largest = -1;
|
||||
double a_largest = 0;
|
||||
for (int i = int(other_layer.lslices_ex.size()) - 1; i >= 0; -- i)
|
||||
if (contour_aabb.overlap(other_layer.lslices_ex[i].bbox))
|
||||
// it is potentially slow, but should be executed rarely
|
||||
if (Polygons overlap = intersection(contour_poly, other_layer.lslices[i]); ! overlap.empty())
|
||||
if (other_has_duplicates) {
|
||||
// Find the contour with the largest overlap. It is expected that the other overlap will be very small.
|
||||
double a = area(overlap);
|
||||
if (a > a_largest) {
|
||||
a_largest = a;
|
||||
i_largest = i;
|
||||
}
|
||||
} else {
|
||||
// Most likely there is just one contour that overlaps, however it is not guaranteed.
|
||||
i_largest = i;
|
||||
break;
|
||||
}
|
||||
assert(i_largest >= 0);
|
||||
return i_largest;
|
||||
}
|
||||
|
||||
const std::vector<std::pair<coord_t, coord_t>> &m_intersections;
|
||||
Layer &m_below;
|
||||
Layer &m_above;
|
||||
@ -340,9 +516,9 @@ static void connect_layer_slices(
|
||||
void Layer::build_up_down_graph(Layer& below, Layer& above)
|
||||
{
|
||||
coord_t paths_below_offset = 0;
|
||||
ClipperLib_Z::Paths paths_below = expolygons_to_zpaths(below.lslices, paths_below_offset);
|
||||
ClipperLib_Z::Paths paths_below = expolygons_to_zpaths_shrunk(below.lslices, paths_below_offset);
|
||||
coord_t paths_above_offset = paths_below_offset + coord_t(below.lslices.size());
|
||||
ClipperLib_Z::Paths paths_above = expolygons_to_zpaths(above.lslices, paths_above_offset);
|
||||
ClipperLib_Z::Paths paths_above = expolygons_to_zpaths_shrunk(above.lslices, paths_above_offset);
|
||||
#ifndef NDEBUG
|
||||
coord_t paths_end = paths_above_offset + coord_t(above.lslices.size());
|
||||
#endif // NDEBUG
|
||||
|
@ -1,6 +1,7 @@
|
||||
#ifndef slic3r_Layer_hpp_
|
||||
#define slic3r_Layer_hpp_
|
||||
|
||||
#include "Line.hpp"
|
||||
#include "libslic3r.h"
|
||||
#include "BoundingBox.hpp"
|
||||
#include "Flow.hpp"
|
||||
@ -325,7 +326,7 @@ public:
|
||||
coordf_t bottom_z() const { return this->print_z - this->height; }
|
||||
|
||||
//Extrusions estimated to be seriously malformed, estimated during "Estimating curled extrusions" step. These lines should be avoided during fast travels.
|
||||
Lines malformed_lines;
|
||||
CurledLines curled_lines;
|
||||
|
||||
// Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry
|
||||
// (with possibly differing extruder ID and slicing parameters) and merged.
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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];
|
||||
|
@ -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.
|
||||
|
@ -57,7 +57,7 @@ void Point::rotate(double angle, const Point ¢er)
|
||||
(*this)(1) = (coord_t)round( (double)center(1) + c * dy + s * dx );
|
||||
}
|
||||
|
||||
bool has_duplicate_points(std::vector<Point> &&pts)
|
||||
bool has_duplicate_points(Points &&pts)
|
||||
{
|
||||
std::sort(pts.begin(), pts.end());
|
||||
for (size_t i = 1; i < pts.size(); ++ i)
|
||||
@ -97,15 +97,15 @@ template BoundingBox get_extents<true>(const Points &pts);
|
||||
// if IncludeBoundary, then a bounding box is defined even for a single point.
|
||||
// otherwise a bounding box is only defined if it has a positive area.
|
||||
template<bool IncludeBoundary>
|
||||
BoundingBox get_extents(const std::vector<Points> &pts)
|
||||
BoundingBox get_extents(const VecOfPoints &pts)
|
||||
{
|
||||
BoundingBox bbox;
|
||||
for (const Points &p : pts)
|
||||
bbox.merge(get_extents<IncludeBoundary>(p));
|
||||
return bbox;
|
||||
}
|
||||
template BoundingBox get_extents<false>(const std::vector<Points> &pts);
|
||||
template BoundingBox get_extents<true>(const std::vector<Points> &pts);
|
||||
template BoundingBox get_extents<false>(const VecOfPoints &pts);
|
||||
template BoundingBox get_extents<true>(const VecOfPoints &pts);
|
||||
|
||||
BoundingBoxf get_extents(const std::vector<Vec2d> &pts)
|
||||
{
|
||||
|
@ -9,6 +9,9 @@
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <oneapi/tbb/scalable_allocator.h>
|
||||
|
||||
|
||||
#include <Eigen/Geometry>
|
||||
|
||||
#include "LocalesUtils.hpp"
|
||||
@ -49,7 +52,10 @@ using Vec2d = Eigen::Matrix<double, 2, 1, Eigen::DontAlign>;
|
||||
using Vec3d = Eigen::Matrix<double, 3, 1, Eigen::DontAlign>;
|
||||
using Vec4d = Eigen::Matrix<double, 4, 1, Eigen::DontAlign>;
|
||||
|
||||
using Points = std::vector<Point>;
|
||||
template<typename BaseType>
|
||||
using PointsAllocator = tbb::scalable_allocator<BaseType>;
|
||||
//using PointsAllocator = std::allocator<BaseType>;
|
||||
using Points = std::vector<Point, PointsAllocator<Point>>;
|
||||
using PointPtrs = std::vector<Point*>;
|
||||
using PointConstPtrs = std::vector<const Point*>;
|
||||
using Points3 = std::vector<Vec3crd>;
|
||||
@ -57,6 +63,8 @@ using Pointfs = std::vector<Vec2d>;
|
||||
using Vec2ds = std::vector<Vec2d>;
|
||||
using Pointf3s = std::vector<Vec3d>;
|
||||
|
||||
using VecOfPoints = std::vector<Points, PointsAllocator<Points>>;
|
||||
|
||||
using Matrix2f = Eigen::Matrix<float, 2, 2, Eigen::DontAlign>;
|
||||
using Matrix2d = Eigen::Matrix<double, 2, 2, Eigen::DontAlign>;
|
||||
using Matrix3f = Eigen::Matrix<float, 3, 3, Eigen::DontAlign>;
|
||||
@ -247,9 +255,9 @@ extern template BoundingBox get_extents<true>(const Points &pts);
|
||||
// if IncludeBoundary, then a bounding box is defined even for a single point.
|
||||
// otherwise a bounding box is only defined if it has a positive area.
|
||||
template<bool IncludeBoundary = false>
|
||||
BoundingBox get_extents(const std::vector<Points> &pts);
|
||||
extern template BoundingBox get_extents<false>(const std::vector<Points> &pts);
|
||||
extern template BoundingBox get_extents<true>(const std::vector<Points> &pts);
|
||||
BoundingBox get_extents(const VecOfPoints &pts);
|
||||
extern template BoundingBox get_extents<false>(const VecOfPoints &pts);
|
||||
extern template BoundingBox get_extents<true>(const VecOfPoints &pts);
|
||||
|
||||
BoundingBoxf get_extents(const std::vector<Vec2d> &pts);
|
||||
|
||||
@ -263,16 +271,16 @@ inline std::pair<Point, bool> nearest_point(const Points &points, const Point &p
|
||||
|
||||
// Test for duplicate points in a vector of points.
|
||||
// The points are copied, sorted and checked for duplicates globally.
|
||||
bool has_duplicate_points(std::vector<Point> &&pts);
|
||||
inline bool has_duplicate_points(const std::vector<Point> &pts)
|
||||
bool has_duplicate_points(Points &&pts);
|
||||
inline bool has_duplicate_points(const Points &pts)
|
||||
{
|
||||
std::vector<Point> cpy = pts;
|
||||
Points cpy = pts;
|
||||
return has_duplicate_points(std::move(cpy));
|
||||
}
|
||||
|
||||
// Test for duplicate points in a vector of points.
|
||||
// Only successive points are checked for equality.
|
||||
inline bool has_duplicate_successive_points(const std::vector<Point> &pts)
|
||||
inline bool has_duplicate_successive_points(const Points &pts)
|
||||
{
|
||||
for (size_t i = 1; i < pts.size(); ++ i)
|
||||
if (pts[i - 1] == pts[i])
|
||||
@ -282,7 +290,7 @@ inline bool has_duplicate_successive_points(const std::vector<Point> &pts)
|
||||
|
||||
// Test for duplicate points in a vector of points.
|
||||
// Only successive points are checked for equality. Additionally, first and last points are compared for equality.
|
||||
inline bool has_duplicate_successive_points_closed(const std::vector<Point> &pts)
|
||||
inline bool has_duplicate_successive_points_closed(const Points &pts)
|
||||
{
|
||||
return has_duplicate_successive_points(pts) || (pts.size() >= 2 && pts.front() == pts.back());
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include "Polygon.hpp"
|
||||
#include "Polyline.hpp"
|
||||
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
double Polygon::length() const
|
||||
@ -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;
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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.
|
||||
|
@ -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))
|
||||
|
@ -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, ®ion, region_id](const tbb::blocked_range<size_t>& range) {
|
||||
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
|
||||
m_print->throw_if_canceled();
|
||||
LayerRegion &layerm = *m_layers[layer_idx]->get_region(region_id);
|
||||
@ -237,6 +252,7 @@ void PrintObject::make_perimeters()
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, m_layers.size()),
|
||||
[this](const tbb::blocked_range<size_t>& range) {
|
||||
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
|
||||
m_print->throw_if_canceled();
|
||||
m_layers[layer_idx]->make_perimeters();
|
||||
@ -408,6 +424,7 @@ void PrintObject::infill()
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, m_layers.size()),
|
||||
[this, &adaptive_fill_octree = adaptive_fill_octree, &support_fill_octree = support_fill_octree](const tbb::blocked_range<size_t>& range) {
|
||||
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
|
||||
m_print->throw_if_canceled();
|
||||
m_layers[layer_idx]->make_fills(adaptive_fill_octree.get(), support_fill_octree.get(), this->m_lightning_generator.get());
|
||||
@ -431,6 +448,7 @@ void PrintObject::ironing()
|
||||
// Ironing starting with layer 0 to support ironing all surfaces.
|
||||
tbb::blocked_range<size_t>(0, m_layers.size()),
|
||||
[this](const tbb::blocked_range<size_t>& range) {
|
||||
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
|
||||
m_print->throw_if_canceled();
|
||||
m_layers[layer_idx]->make_ironing();
|
||||
@ -491,7 +509,9 @@ void PrintObject::generate_support_material()
|
||||
void PrintObject::estimate_curled_extrusions()
|
||||
{
|
||||
if (this->set_started(posEstimateCurledExtrusions)) {
|
||||
if (this->print()->config().avoid_crossing_curled_overhangs) {
|
||||
if (this->print()->config().avoid_crossing_curled_overhangs ||
|
||||
std::any_of(this->print()->m_print_regions.begin(), this->print()->m_print_regions.end(),
|
||||
[](const PrintRegion *region) { return region->config().enable_dynamic_overhang_speeds.getBool(); })) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "Estimating areas with curled extrusions - start";
|
||||
m_print->set_status(88, _u8L("Estimating curled extrusions"));
|
||||
|
||||
@ -528,16 +548,17 @@ std::pair<FillAdaptive::OctreePtr, FillAdaptive::OctreePtr> PrintObject::prepare
|
||||
std::vector<std::vector<Vec3d>> overhangs(std::max(surfaces_w_bottom_z.size(), size_t(1)));
|
||||
// ^ make sure vector is not empty, even with no briding surfaces we still want to build the adaptive trees later, some continue normally
|
||||
tbb::parallel_for(tbb::blocked_range<int>(0, surfaces_w_bottom_z.size()),
|
||||
[this, &to_octree, &overhangs, &surfaces_w_bottom_z](const tbb::blocked_range<int> &range) {
|
||||
for (int surface_idx = range.begin(); surface_idx < range.end(); ++surface_idx) {
|
||||
std::vector<Vec3d> &out = overhangs[surface_idx];
|
||||
m_print->throw_if_canceled();
|
||||
append(out, triangulate_expolygon_3d(surfaces_w_bottom_z[surface_idx].first->expolygon,
|
||||
surfaces_w_bottom_z[surface_idx].second));
|
||||
for (Vec3d &p : out)
|
||||
p = (to_octree * p).eval();
|
||||
}
|
||||
});
|
||||
[this, &to_octree, &overhangs, &surfaces_w_bottom_z](const tbb::blocked_range<int> &range) {
|
||||
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
|
||||
for (int surface_idx = range.begin(); surface_idx < range.end(); ++surface_idx) {
|
||||
std::vector<Vec3d> &out = overhangs[surface_idx];
|
||||
m_print->throw_if_canceled();
|
||||
append(out, triangulate_expolygon_3d(surfaces_w_bottom_z[surface_idx].first->expolygon,
|
||||
surfaces_w_bottom_z[surface_idx].second));
|
||||
for (Vec3d &p : out)
|
||||
p = (to_octree * p).eval();
|
||||
}
|
||||
});
|
||||
// and gather them.
|
||||
for (size_t i = 1; i < overhangs.size(); ++ i)
|
||||
append(overhangs.front(), std::move(overhangs[i]));
|
||||
@ -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.
|
||||
|
@ -43,7 +43,8 @@ Point ConcaveHull::centroid(const Points &pp)
|
||||
Points ConcaveHull::calculate_centroids() const
|
||||
{
|
||||
// We get the centroids of all the islands in the 2D slice
|
||||
Points centroids = reserve_vector<Point>(m_polys.size());
|
||||
Points centroids;
|
||||
centroids.reserve(m_polys.size());
|
||||
std::transform(m_polys.begin(), m_polys.end(),
|
||||
std::back_inserter(centroids),
|
||||
[](const Polygon &poly) { return centroid(poly); });
|
||||
|
@ -6,6 +6,8 @@
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
|
||||
#include <libslic3r/Point.hpp>
|
||||
|
||||
struct indexed_triangle_set;
|
||||
|
||||
namespace Slic3r {
|
||||
@ -13,7 +15,7 @@ namespace Slic3r {
|
||||
class ExPolygon;
|
||||
class Polygon;
|
||||
using ExPolygons = std::vector<ExPolygon>;
|
||||
using Polygons = std::vector<Polygon>;
|
||||
using Polygons = std::vector<Polygon, PointsAllocator<Polygon>>;
|
||||
|
||||
namespace sla {
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include <libslic3r/Polygon.hpp>
|
||||
#include <libslic3r/ExPolygon.hpp>
|
||||
#include <libslic3r/AABBMesh.hpp>
|
||||
|
||||
@ -14,9 +15,6 @@
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
using Polygons = std::vector<Polygon>;
|
||||
using ExPolygons = std::vector<ExPolygon>;
|
||||
|
||||
namespace sla {
|
||||
|
||||
struct SupportTreeConfig
|
||||
|
@ -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!";
|
||||
|
@ -6,6 +6,8 @@
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
void its_short_edge_collpase(indexed_triangle_set &mesh, size_t target_triangle_count) {
|
||||
@ -155,7 +157,7 @@ void its_short_edge_collpase(indexed_triangle_set &mesh, size_t target_triangle_
|
||||
}
|
||||
|
||||
//Extract the result mesh
|
||||
std::unordered_map<size_t, size_t> final_vertices_mapping;
|
||||
ankerl::unordered_dense::map<size_t, size_t> final_vertices_mapping;
|
||||
std::vector<Vec3f> final_vertices;
|
||||
std::vector<Vec3i> final_indices;
|
||||
final_indices.reserve(face_indices.size());
|
||||
|
@ -8,10 +8,13 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace ClipperLib { class PolyNode; }
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace ClipperLib {
|
||||
class PolyNode;
|
||||
using PolyNodes = std::vector<PolyNode*, PointsAllocator<PolyNode*>>;
|
||||
}
|
||||
|
||||
class ExPolygon;
|
||||
using ExPolygons = std::vector<ExPolygon>;
|
||||
|
||||
@ -29,7 +32,7 @@ void chain_and_reorder_extrusion_paths(std::vect
|
||||
Polylines chain_polylines(Polylines &&src, const Point *start_near = nullptr);
|
||||
inline Polylines chain_polylines(const Polylines& src, const Point* start_near = nullptr) { Polylines tmp(src); return chain_polylines(std::move(tmp), start_near); }
|
||||
|
||||
std::vector<ClipperLib::PolyNode*> chain_clipper_polynodes(const Points &points, const std::vector<ClipperLib::PolyNode*> &items);
|
||||
ClipperLib::PolyNodes chain_clipper_polynodes(const Points &points, const ClipperLib::PolyNodes &items);
|
||||
|
||||
// Chain instances of print objects by an approximate shortest path.
|
||||
// Returns pairs of PrintObject idx and instance of that PrintObject.
|
||||
|
1999
src/libslic3r/Support/SupportCommon.cpp
Normal file
1999
src/libslic3r/Support/SupportCommon.cpp
Normal file
File diff suppressed because it is too large
Load Diff
155
src/libslic3r/Support/SupportCommon.hpp
Normal file
155
src/libslic3r/Support/SupportCommon.hpp
Normal 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_ */
|
108
src/libslic3r/Support/SupportDebug.cpp
Normal file
108
src/libslic3r/Support/SupportDebug.cpp
Normal 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 */
|
18
src/libslic3r/Support/SupportDebug.hpp
Normal file
18
src/libslic3r/Support/SupportDebug.hpp
Normal 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_ */
|
146
src/libslic3r/Support/SupportLayer.hpp
Normal file
146
src/libslic3r/Support/SupportLayer.hpp
Normal 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_ */
|
File diff suppressed because it is too large
Load Diff
101
src/libslic3r/Support/SupportMaterial.hpp
Normal file
101
src/libslic3r/Support/SupportMaterial.hpp
Normal 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_ */
|
144
src/libslic3r/Support/SupportParameters.cpp
Normal file
144
src/libslic3r/Support/SupportParameters.cpp
Normal 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 ®ion = 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
|
96
src/libslic3r/Support/SupportParameters.hpp
Normal file
96
src/libslic3r/Support/SupportParameters.hpp
Normal 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_ */
|
@ -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 ¤t = 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();
|
||||
}
|
||||
});
|
@ -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();
|
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
}
|
||||
|
||||
/*!
|
@ -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_ */
|
@ -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 ¶ms)
|
||||
{
|
||||
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 ¶ms)
|
||||
std::tuple<SupportPoints, PartialObjects> check_stability(const PrintObject *po,
|
||||
const PrecomputedSliceConnections &precomputed_slices_connections,
|
||||
const PrintTryCancel &cancel_func,
|
||||
const Params ¶ms)
|
||||
{
|
||||
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, ¶ms](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 ¶ms)
|
||||
{
|
||||
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 ¶ms)
|
||||
{
|
||||
#ifdef DEBUG_FILES
|
||||
FILE *debug_file = boost::nowide::fopen(debug_out_path("object_malformations.obj").c_str(), "w");
|
||||
FILE *full_file = boost::nowide::fopen(debug_out_path("object_full.obj").c_str(), "w");
|
||||
FILE *full_file = boost::nowide::fopen(debug_out_path("object_full.obj").c_str(), "w");
|
||||
#endif
|
||||
|
||||
LD prev_layer_lines{};
|
||||
|
||||
for (Layer *l : layers) {
|
||||
l->malformed_lines.clear();
|
||||
l->curled_lines.clear();
|
||||
std::vector<Linef> boundary_lines = l->lower_layer != nullptr ? to_unscaled_linesf(l->lower_layer->lslices) : std::vector<Linef>();
|
||||
AABBTreeLines::LinesDistancer<Linef> prev_layer_boundary{std::move(boundary_lines)};
|
||||
std::vector<ExtrusionLine> current_layer_lines;
|
||||
for (const LayerRegion *layer_region : l->regions()) {
|
||||
for (const ExtrusionEntity *extrusion : layer_region->perimeters().flatten().entities) {
|
||||
|
||||
if (!extrusion->role().is_external_perimeter()) continue;
|
||||
if (!extrusion->role().is_external_perimeter())
|
||||
continue;
|
||||
|
||||
Points extrusion_pts;
|
||||
extrusion->collect_points(extrusion_pts);
|
||||
float flow_width = get_flow_width(layer_region, extrusion->role());
|
||||
auto annotated_points = estimate_points_properties<true, false, false, false>(extrusion_pts, prev_layer_lines, flow_width,
|
||||
params.bridge_distance);
|
||||
auto annotated_points = estimate_points_properties<true, true, false, false>(extrusion_pts, prev_layer_lines, flow_width,
|
||||
params.bridge_distance);
|
||||
for (size_t i = 0; i < annotated_points.size(); ++i) {
|
||||
ExtendedPoint &curr_point = annotated_points[i];
|
||||
float line_len = i > 0 ? ((annotated_points[i - 1].position - curr_point.position).norm()) : 0.0f;
|
||||
ExtrusionLine line_out{i > 0 ? annotated_points[i - 1].position.cast<float>() : curr_point.position.cast<float>(),
|
||||
curr_point.position.cast<float>(), line_len, extrusion};
|
||||
const ExtendedPoint &a = i > 0 ? annotated_points[i - 1] : annotated_points[i];
|
||||
const ExtendedPoint &b = annotated_points[i];
|
||||
ExtrusionLine line_out{a.position.cast<float>(), b.position.cast<float>(), float((a.position - b.position).norm()),
|
||||
extrusion};
|
||||
|
||||
const ExtrusionLine nearest_prev_layer_line = prev_layer_lines.get_lines().size() > 0 ?
|
||||
prev_layer_lines.get_line(curr_point.nearest_prev_layer_line) :
|
||||
ExtrusionLine{};
|
||||
Vec2f middle = 0.5 * (line_out.a + line_out.b);
|
||||
auto [middle_distance, bottom_line_idx, x] = prev_layer_lines.distance_from_lines_extra<false>(middle);
|
||||
ExtrusionLine bottom_line = prev_layer_lines.get_lines().empty() ? ExtrusionLine{} :
|
||||
prev_layer_lines.get_line(bottom_line_idx);
|
||||
|
||||
float sign = (prev_layer_boundary.distance_from_lines<true>(curr_point.position) + 0.5f * flow_width) < 0.0f ? -1.0f :
|
||||
1.0f;
|
||||
curr_point.distance *= sign;
|
||||
// correctify the distance sign using slice polygons
|
||||
float sign = (prev_layer_boundary.distance_from_lines<true>(middle.cast<double>()) + 0.5f * flow_width) < 0.0f ? -1.0f : 1.0f;
|
||||
|
||||
line_out.curled_up_height = estimate_curled_up_height(curr_point, layer_region->layer()->height, flow_width,
|
||||
nearest_prev_layer_line.curled_up_height, params);
|
||||
line_out.curled_up_height = estimate_curled_up_height(middle_distance * sign, 0.5 * (a.curvature + b.curvature),
|
||||
l->height, flow_width, bottom_line.curled_up_height, params);
|
||||
|
||||
current_layer_lines.push_back(line_out);
|
||||
}
|
||||
@ -1153,7 +1205,7 @@ void estimate_malformations(LayerPtrs &layers, const Params ¶ms)
|
||||
|
||||
for (const ExtrusionLine &line : current_layer_lines) {
|
||||
if (line.curled_up_height > params.curling_tolerance_limit) {
|
||||
l->malformed_lines.push_back(Line{Point::new_scale(line.a), Point::new_scale(line.b)});
|
||||
l->curled_lines.push_back(CurledLine{Point::new_scale(line.a), Point::new_scale(line.b), line.curled_up_height});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1164,9 +1216,9 @@ void estimate_malformations(LayerPtrs &layers, const Params ¶ms)
|
||||
fprintf(debug_file, "v %f %f %f %f %f %f\n", line.b[0], line.b[1], l->print_z, color[0], color[1], color[2]);
|
||||
}
|
||||
}
|
||||
for (const ExtrusionLine &line : current_layer_lines) {
|
||||
Vec3f color = value_to_rgbf(-EPSILON, l->height * params.max_curled_height_factor, line.curled_up_height);
|
||||
fprintf(full_file, "v %f %f %f %f %f %f\n", line.b[0], line.b[1], l->print_z, color[0], color[1], color[2]);
|
||||
for (const ExtrusionLine &line : current_layer_lines) {
|
||||
Vec3f color = value_to_rgbf(-EPSILON, l->height * params.max_curled_height_factor, line.curled_up_height);
|
||||
fprintf(full_file, "v %f %f %f %f %f %f\n", line.b[0], line.b[1], l->print_z, color[0], color[1], color[2]);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user