Merge remote-tracking branch 'PRIVATE/master' into ys_cut
@ -34,6 +34,7 @@ option(SLIC3R_MSVC_PDB "Generate PDB files on MSVC in Release mode" 1)
|
|||||||
option(SLIC3R_PERL_XS "Compile XS Perl module and enable Perl unit and integration tests" 0)
|
option(SLIC3R_PERL_XS "Compile XS Perl module and enable Perl unit and integration tests" 0)
|
||||||
option(SLIC3R_ASAN "Enable ASan on Clang and GCC" 0)
|
option(SLIC3R_ASAN "Enable ASan on Clang and GCC" 0)
|
||||||
option(SLIC3R_UBSAN "Enable UBSan on Clang and GCC" 0)
|
option(SLIC3R_UBSAN "Enable UBSan on Clang and GCC" 0)
|
||||||
|
option(SLIC3R_ENABLE_FORMAT_STEP "Enable compilation of STEP file support" 1)
|
||||||
# If SLIC3R_FHS is 1 -> SLIC3R_DESKTOP_INTEGRATION is always 0, othrewise variable.
|
# If SLIC3R_FHS is 1 -> SLIC3R_DESKTOP_INTEGRATION is always 0, othrewise variable.
|
||||||
CMAKE_DEPENDENT_OPTION(SLIC3R_DESKTOP_INTEGRATION "Allow perfoming desktop integration during runtime" 1 "NOT SLIC3R_FHS" 0)
|
CMAKE_DEPENDENT_OPTION(SLIC3R_DESKTOP_INTEGRATION "Allow perfoming desktop integration during runtime" 1 "NOT SLIC3R_FHS" 0)
|
||||||
|
|
||||||
|
5
deps/CMakeLists.txt
vendored
@ -75,6 +75,9 @@ function(prusaslicer_add_cmake_project projectname)
|
|||||||
if (MSVC)
|
if (MSVC)
|
||||||
set(_gen CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}")
|
set(_gen CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}")
|
||||||
set(_build_j "/m")
|
set(_build_j "/m")
|
||||||
|
if (${projectname} STREQUAL "OCCT")
|
||||||
|
set(_build_j "/m:1")
|
||||||
|
endif ()
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
ExternalProject_Add(
|
ExternalProject_Add(
|
||||||
@ -190,6 +193,7 @@ include(JPEG/JPEG.cmake)
|
|||||||
include(TIFF/TIFF.cmake)
|
include(TIFF/TIFF.cmake)
|
||||||
include(NanoSVG/NanoSVG.cmake)
|
include(NanoSVG/NanoSVG.cmake)
|
||||||
include(wxWidgets/wxWidgets.cmake)
|
include(wxWidgets/wxWidgets.cmake)
|
||||||
|
include(OCCT/OCCT.cmake)
|
||||||
|
|
||||||
set(_dep_list
|
set(_dep_list
|
||||||
dep_Boost
|
dep_Boost
|
||||||
@ -202,6 +206,7 @@ set(_dep_list
|
|||||||
dep_OpenCSG
|
dep_OpenCSG
|
||||||
dep_CGAL
|
dep_CGAL
|
||||||
dep_Qhull
|
dep_Qhull
|
||||||
|
dep_OCCT
|
||||||
${PNG_PKG}
|
${PNG_PKG}
|
||||||
${ZLIB_PKG}
|
${ZLIB_PKG}
|
||||||
${EXPAT_PKG}
|
${EXPAT_PKG}
|
||||||
|
22
deps/OCCT/OCCT.cmake
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
prusaslicer_add_cmake_project(OCCT
|
||||||
|
#LMBBS: changed version to 7.6.2
|
||||||
|
URL https://github.com/Open-Cascade-SAS/OCCT/archive/refs/tags/V7_6_2.zip
|
||||||
|
URL_HASH SHA256=c696b923593e8c18d059709717dbf155b3e72fdd283c8522047a790ec3a432c5
|
||||||
|
|
||||||
|
CMAKE_ARGS
|
||||||
|
-DINSTALL_DIR_LAYOUT=Unix # LMBBS
|
||||||
|
-DBUILD_LIBRARY_TYPE=Static
|
||||||
|
-DUSE_TK=OFF
|
||||||
|
-DUSE_TBB=OFF
|
||||||
|
-DUSE_FREETYPE=OFF
|
||||||
|
-DUSE_FFMPEG=OFF
|
||||||
|
-DUSE_VTK=OFF
|
||||||
|
-DUSE_FREETYPE=OFF
|
||||||
|
-DBUILD_MODULE_ApplicationFramework=OFF
|
||||||
|
#-DBUILD_MODULE_DataExchange=OFF
|
||||||
|
-DBUILD_MODULE_Draw=OFF
|
||||||
|
-DBUILD_MODULE_FoundationClasses=OFF
|
||||||
|
-DBUILD_MODULE_ModelingAlgorithms=OFF
|
||||||
|
-DBUILD_MODULE_ModelingData=OFF
|
||||||
|
-DBUILD_MODULE_Visualization=OFF
|
||||||
|
)
|
@ -1,4 +1,5 @@
|
|||||||
min_slic3r_version = 2.5.0-alpha0
|
min_slic3r_version = 2.5.0-alpha0
|
||||||
|
0.2.0 Added alternative nozzle support
|
||||||
0.1.5 Added Ender-3 S1 Pro
|
0.1.5 Added Ender-3 S1 Pro
|
||||||
min_slic3r_version = 2.4.1
|
min_slic3r_version = 2.4.1
|
||||||
0.1.4 Added Ender-3 Pro. Added M25 support for some printers.
|
0.1.4 Added Ender-3 Pro. Added M25 support for some printers.
|
||||||
|
2
resources/profiles/Elegoo.idx
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
min_slic3r_version = 2.5.0-alpha3
|
||||||
|
1.0.0 Initial version
|
510
resources/profiles/Elegoo.ini
Normal file
@ -0,0 +1,510 @@
|
|||||||
|
# PrusaSlicer print profiles for the Elegoo printers.
|
||||||
|
# By Andrew Suzuki (andrewsuzuki.com), adapted from Creality.ini
|
||||||
|
|
||||||
|
[vendor]
|
||||||
|
# Vendor name will be shown by the Config Wizard.
|
||||||
|
name = Elegoo
|
||||||
|
# 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.0
|
||||||
|
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Elegoo/
|
||||||
|
|
||||||
|
# The printer models will be shown by the Configuration Wizard in this order,
|
||||||
|
# also the first model installed & the first nozzle installed will be activated after install.
|
||||||
|
# Printer model name will be shown by the installation wizard.
|
||||||
|
|
||||||
|
[printer_model:NEPTUNE1]
|
||||||
|
name = Elegoo Neptune-1
|
||||||
|
variants = 0.4
|
||||||
|
technology = FFF
|
||||||
|
family = NEPTUNE
|
||||||
|
bed_model =
|
||||||
|
bed_texture =
|
||||||
|
default_materials = Generic PLA @ELEGOO; Generic PETG @ELEGOO; Generic ABS @ELEGOO
|
||||||
|
|
||||||
|
[printer_model:NEPTUNE2]
|
||||||
|
name = Elegoo Neptune-2
|
||||||
|
variants = 0.4
|
||||||
|
technology = FFF
|
||||||
|
family = NEPTUNE
|
||||||
|
bed_model =
|
||||||
|
bed_texture =
|
||||||
|
default_materials = Generic PLA @ELEGOO; Generic PETG @ELEGOO; Generic ABS @ELEGOO
|
||||||
|
|
||||||
|
[printer_model:NEPTUNE2D]
|
||||||
|
name = Elegoo Neptune-2D
|
||||||
|
variants = 0.4
|
||||||
|
technology = FFF
|
||||||
|
family = NEPTUNE
|
||||||
|
bed_model =
|
||||||
|
bed_texture =
|
||||||
|
default_materials = Generic PLA @ELEGOO; Generic PETG @ELEGOO; Generic ABS @ELEGOO
|
||||||
|
|
||||||
|
[printer_model:NEPTUNE2S]
|
||||||
|
name = Elegoo Neptune-2S
|
||||||
|
variants = 0.4
|
||||||
|
technology = FFF
|
||||||
|
family = NEPTUNE
|
||||||
|
bed_model =
|
||||||
|
bed_texture =
|
||||||
|
default_materials = Generic PLA @ELEGOO; Generic PETG @ELEGOO; Generic ABS @ELEGOO
|
||||||
|
|
||||||
|
[printer_model:NEPTUNE3]
|
||||||
|
name = Elegoo Neptune-3
|
||||||
|
variants = 0.4
|
||||||
|
technology = FFF
|
||||||
|
family = NEPTUNE
|
||||||
|
bed_model =
|
||||||
|
bed_texture =
|
||||||
|
default_materials = Generic PLA @ELEGOO; Generic PETG @ELEGOO; Generic ABS @ELEGOO
|
||||||
|
|
||||||
|
[printer_model:NEPTUNEX]
|
||||||
|
name = Elegoo Neptune-X
|
||||||
|
variants = 0.4
|
||||||
|
technology = FFF
|
||||||
|
family = NEPTUNE
|
||||||
|
bed_model =
|
||||||
|
bed_texture =
|
||||||
|
default_materials = Generic PLA @ELEGOO; Generic PETG @ELEGOO; Generic ABS @ELEGOO
|
||||||
|
|
||||||
|
# All presets starting with asterisk, for example *common*, are intermediate and they will
|
||||||
|
# not make it into the user interface.
|
||||||
|
|
||||||
|
# Common print preset
|
||||||
|
[print:*common*]
|
||||||
|
avoid_crossing_perimeters = 0
|
||||||
|
bridge_angle = 0
|
||||||
|
bridge_flow_ratio = 0.95
|
||||||
|
bridge_speed = 25
|
||||||
|
brim_width = 0
|
||||||
|
clip_multipart_objects = 1
|
||||||
|
compatible_printers =
|
||||||
|
complete_objects = 0
|
||||||
|
dont_support_bridges = 1
|
||||||
|
elefant_foot_compensation = 0.1
|
||||||
|
ensure_vertical_shell_thickness = 1
|
||||||
|
external_fill_pattern = rectilinear
|
||||||
|
external_perimeters_first = 0
|
||||||
|
external_perimeter_extrusion_width = 0.45
|
||||||
|
external_perimeter_speed = 25
|
||||||
|
extra_perimeters = 0
|
||||||
|
extruder_clearance_height = 25
|
||||||
|
extruder_clearance_radius = 45
|
||||||
|
extrusion_width = 0.45
|
||||||
|
fill_angle = 45
|
||||||
|
fill_density = 20%
|
||||||
|
fill_pattern = grid
|
||||||
|
first_layer_extrusion_width = 0.42
|
||||||
|
first_layer_height = 0.2
|
||||||
|
first_layer_speed = 20
|
||||||
|
gap_fill_speed = 30
|
||||||
|
gcode_comments = 0
|
||||||
|
infill_every_layers = 1
|
||||||
|
infill_extruder = 1
|
||||||
|
infill_extrusion_width = 0.45
|
||||||
|
infill_first = 0
|
||||||
|
infill_only_where_needed = 0
|
||||||
|
infill_overlap = 25%
|
||||||
|
infill_speed = 50
|
||||||
|
interface_shells = 0
|
||||||
|
max_print_speed = 100
|
||||||
|
max_volumetric_extrusion_rate_slope_negative = 0
|
||||||
|
max_volumetric_extrusion_rate_slope_positive = 0
|
||||||
|
max_volumetric_speed = 0
|
||||||
|
min_skirt_length = 4
|
||||||
|
notes =
|
||||||
|
overhangs = 0
|
||||||
|
only_retract_when_crossing_perimeters = 0
|
||||||
|
ooze_prevention = 0
|
||||||
|
output_filename_format = {input_filename_base}_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode
|
||||||
|
perimeters = 2
|
||||||
|
perimeter_extruder = 1
|
||||||
|
perimeter_extrusion_width = 0.45
|
||||||
|
perimeter_speed = 40
|
||||||
|
post_process =
|
||||||
|
print_settings_id =
|
||||||
|
raft_layers = 0
|
||||||
|
resolution = 0
|
||||||
|
seam_position = nearest
|
||||||
|
single_extruder_multi_material_priming = 0
|
||||||
|
skirts = 1
|
||||||
|
skirt_distance = 2
|
||||||
|
skirt_height = 2
|
||||||
|
small_perimeter_speed = 25
|
||||||
|
solid_infill_below_area = 0
|
||||||
|
solid_infill_every_layers = 0
|
||||||
|
solid_infill_extruder = 1
|
||||||
|
solid_infill_extrusion_width = 0.45
|
||||||
|
solid_infill_speed = 40
|
||||||
|
spiral_vase = 0
|
||||||
|
standby_temperature_delta = -5
|
||||||
|
support_material = 0
|
||||||
|
support_material_extruder = 0
|
||||||
|
support_material_extrusion_width = 0.38
|
||||||
|
support_material_interface_extruder = 0
|
||||||
|
support_material_angle = 0
|
||||||
|
support_material_buildplate_only = 0
|
||||||
|
support_material_enforce_layers = 0
|
||||||
|
support_material_contact_distance = 0.15
|
||||||
|
support_material_interface_contact_loops = 0
|
||||||
|
support_material_interface_layers = 2
|
||||||
|
support_material_interface_spacing = 0.2
|
||||||
|
support_material_interface_speed = 100%
|
||||||
|
support_material_pattern = rectilinear
|
||||||
|
support_material_spacing = 2
|
||||||
|
support_material_speed = 40
|
||||||
|
support_material_synchronize_layers = 0
|
||||||
|
support_material_threshold = 45
|
||||||
|
support_material_with_sheath = 0
|
||||||
|
support_material_xy_spacing = 60%
|
||||||
|
thin_walls = 0
|
||||||
|
top_infill_extrusion_width = 0.4
|
||||||
|
top_solid_infill_speed = 30
|
||||||
|
travel_speed = 150
|
||||||
|
wipe_tower = 0
|
||||||
|
wipe_tower_bridging = 10
|
||||||
|
wipe_tower_rotation_angle = 0
|
||||||
|
wipe_tower_width = 60
|
||||||
|
wipe_tower_x = 170
|
||||||
|
wipe_tower_y = 140
|
||||||
|
xy_size_compensation = 0
|
||||||
|
|
||||||
|
[print:*0.08mm*]
|
||||||
|
inherits = *common*
|
||||||
|
layer_height = 0.08
|
||||||
|
perimeters = 3
|
||||||
|
bottom_solid_layers = 9
|
||||||
|
top_solid_layers = 11
|
||||||
|
|
||||||
|
[print:*0.10mm*]
|
||||||
|
inherits = *common*
|
||||||
|
layer_height = 0.1
|
||||||
|
perimeters = 3
|
||||||
|
bottom_solid_layers = 7
|
||||||
|
top_solid_layers = 9
|
||||||
|
|
||||||
|
[print:*0.12mm*]
|
||||||
|
inherits = *common*
|
||||||
|
layer_height = 0.12
|
||||||
|
perimeters = 3
|
||||||
|
bottom_solid_layers = 6
|
||||||
|
top_solid_layers = 7
|
||||||
|
|
||||||
|
[print:*0.16mm*]
|
||||||
|
inherits = *common*
|
||||||
|
layer_height = 0.16
|
||||||
|
bottom_solid_layers = 5
|
||||||
|
top_solid_layers = 7
|
||||||
|
|
||||||
|
[print:*0.20mm*]
|
||||||
|
inherits = *common*
|
||||||
|
layer_height = 0.20
|
||||||
|
bottom_solid_layers = 4
|
||||||
|
top_solid_layers = 5
|
||||||
|
|
||||||
|
[print:*0.24mm*]
|
||||||
|
inherits = *common*
|
||||||
|
layer_height = 0.24
|
||||||
|
top_infill_extrusion_width = 0.45
|
||||||
|
bottom_solid_layers = 3
|
||||||
|
top_solid_layers = 4
|
||||||
|
|
||||||
|
[print:*0.28mm*]
|
||||||
|
inherits = *common*
|
||||||
|
layer_height = 0.28
|
||||||
|
top_infill_extrusion_width = 0.45
|
||||||
|
bottom_solid_layers = 3
|
||||||
|
top_solid_layers = 4
|
||||||
|
|
||||||
|
[print:0.08mm SUPERDETAIL @ELEGOO]
|
||||||
|
inherits = *0.08mm*
|
||||||
|
compatible_printers_condition = printer_model=~/(NEPTUNE).*/ and nozzle_diameter[0]==0.4
|
||||||
|
|
||||||
|
[print:0.10mm HIGHDETAIL @ELEGOO]
|
||||||
|
inherits = *0.10mm*
|
||||||
|
compatible_printers_condition = printer_model=~/(NEPTUNE).*/ and nozzle_diameter[0]==0.4
|
||||||
|
|
||||||
|
[print:0.12mm DETAIL @ELEGOO]
|
||||||
|
inherits = *0.12mm*
|
||||||
|
compatible_printers_condition = printer_model=~/(NEPTUNE).*/ and nozzle_diameter[0]==0.4
|
||||||
|
|
||||||
|
[print:0.16mm OPTIMAL @ELEGOO]
|
||||||
|
inherits = *0.16mm*
|
||||||
|
compatible_printers_condition = printer_model=~/(NEPTUNE).*/ and nozzle_diameter[0]==0.4
|
||||||
|
|
||||||
|
[print:0.20mm NORMAL @ELEGOO]
|
||||||
|
inherits = *0.20mm*
|
||||||
|
compatible_printers_condition = printer_model=~/(NEPTUNE).*/ and nozzle_diameter[0]==0.4
|
||||||
|
|
||||||
|
[print:0.24mm DRAFT @ELEGOO]
|
||||||
|
inherits = *0.24mm*
|
||||||
|
compatible_printers_condition = printer_model=~/(NEPTUNE).*/ and nozzle_diameter[0]==0.4
|
||||||
|
|
||||||
|
[print:0.28mm SUPERDRAFT @ELEGOO]
|
||||||
|
inherits = *0.28mm*
|
||||||
|
compatible_printers_condition = printer_model=~/(NEPTUNE).*/ and nozzle_diameter[0]==0.4
|
||||||
|
|
||||||
|
# When submitting new filaments please print the following temperature tower at 0.1mm layer height:
|
||||||
|
# https://www.thingiverse.com/thing:2615842
|
||||||
|
# Pay particular attention to bridging, overhangs and retractions.
|
||||||
|
# Also print the following bed adhesion test at 0.1 layer height as well:
|
||||||
|
# https://www.prusaprinters.org/prints/4634-bed-adhesion-warp-test
|
||||||
|
# At least for PLA, please keep bed temp at 60, as many Elegoo printers do not have any ABL
|
||||||
|
# So having some leeway to get good bed adhesion is not a luxury for many users
|
||||||
|
|
||||||
|
[filament:*common*]
|
||||||
|
cooling = 0
|
||||||
|
compatible_printers =
|
||||||
|
extrusion_multiplier = 1
|
||||||
|
filament_cost = 0
|
||||||
|
filament_density = 0
|
||||||
|
filament_diameter = 1.75
|
||||||
|
filament_notes = ""
|
||||||
|
filament_settings_id = ""
|
||||||
|
filament_soluble = 0
|
||||||
|
min_print_speed = 15
|
||||||
|
slowdown_below_layer_time = 20
|
||||||
|
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_ELEGOO.*/
|
||||||
|
|
||||||
|
[filament:*PLA*]
|
||||||
|
inherits = *common*
|
||||||
|
bed_temperature = 60
|
||||||
|
fan_below_layer_time = 100
|
||||||
|
filament_colour = #DDDDDD
|
||||||
|
filament_max_volumetric_speed = 15
|
||||||
|
filament_type = PLA
|
||||||
|
filament_density = 1.24
|
||||||
|
filament_cost = 20
|
||||||
|
first_layer_bed_temperature = 60
|
||||||
|
first_layer_temperature = 210
|
||||||
|
fan_always_on = 1
|
||||||
|
cooling = 1
|
||||||
|
max_fan_speed = 100
|
||||||
|
min_fan_speed = 100
|
||||||
|
bridge_fan_speed = 100
|
||||||
|
disable_fan_first_layers = 1
|
||||||
|
temperature = 205
|
||||||
|
|
||||||
|
[filament:*PET*]
|
||||||
|
inherits = *common*
|
||||||
|
bed_temperature = 70
|
||||||
|
cooling = 1
|
||||||
|
disable_fan_first_layers = 3
|
||||||
|
fan_below_layer_time = 20
|
||||||
|
filament_colour = #DDDDDD
|
||||||
|
filament_max_volumetric_speed = 8
|
||||||
|
filament_type = PETG
|
||||||
|
filament_density = 1.27
|
||||||
|
filament_cost = 20
|
||||||
|
first_layer_bed_temperature = 70
|
||||||
|
first_layer_temperature = 240
|
||||||
|
fan_always_on = 1
|
||||||
|
max_fan_speed = 50
|
||||||
|
min_fan_speed = 20
|
||||||
|
bridge_fan_speed = 100
|
||||||
|
temperature = 240
|
||||||
|
|
||||||
|
[filament:*ABS*]
|
||||||
|
inherits = *common*
|
||||||
|
bed_temperature = 100
|
||||||
|
cooling = 0
|
||||||
|
disable_fan_first_layers = 3
|
||||||
|
fan_below_layer_time = 20
|
||||||
|
filament_colour = #DDDDDD
|
||||||
|
filament_max_volumetric_speed = 11
|
||||||
|
filament_type = ABS
|
||||||
|
filament_density = 1.04
|
||||||
|
filament_cost = 20
|
||||||
|
first_layer_bed_temperature = 100
|
||||||
|
first_layer_temperature = 245
|
||||||
|
fan_always_on = 0
|
||||||
|
max_fan_speed = 0
|
||||||
|
min_fan_speed = 0
|
||||||
|
bridge_fan_speed = 30
|
||||||
|
top_fan_speed = 0
|
||||||
|
temperature = 245
|
||||||
|
|
||||||
|
[filament:Generic PLA @ELEGOO]
|
||||||
|
inherits = *PLA*
|
||||||
|
filament_vendor = Generic
|
||||||
|
|
||||||
|
[filament:Generic PETG @ELEGOO]
|
||||||
|
inherits = *PET*
|
||||||
|
filament_vendor = Generic
|
||||||
|
|
||||||
|
[filament:Generic ABS @ELEGOO]
|
||||||
|
inherits = *ABS*
|
||||||
|
first_layer_bed_temperature = 90
|
||||||
|
bed_temperature = 90
|
||||||
|
filament_vendor = Generic
|
||||||
|
|
||||||
|
# Common printer preset
|
||||||
|
[printer:*common*]
|
||||||
|
printer_technology = FFF
|
||||||
|
before_layer_gcode = ;BEFORE_LAYER_CHANGE\nG92 E0\n;[layer_z]\n\n
|
||||||
|
bed_shape = 0x0,235x0,235x235,0x235
|
||||||
|
between_objects_gcode =
|
||||||
|
pause_print_gcode =
|
||||||
|
deretract_speed = 0
|
||||||
|
extruder_colour = #FCE94F
|
||||||
|
extruder_offset = 0x0
|
||||||
|
gcode_flavor = marlin
|
||||||
|
silent_mode = 0
|
||||||
|
remaining_times = 0
|
||||||
|
machine_max_acceleration_e = 5000
|
||||||
|
machine_max_acceleration_extruding = 500
|
||||||
|
machine_max_acceleration_retracting = 1000
|
||||||
|
machine_max_acceleration_x = 500
|
||||||
|
machine_max_acceleration_y = 500
|
||||||
|
machine_max_acceleration_z = 100
|
||||||
|
machine_max_feedrate_e = 60
|
||||||
|
machine_max_feedrate_x = 500
|
||||||
|
machine_max_feedrate_y = 500
|
||||||
|
machine_max_feedrate_z = 10
|
||||||
|
machine_max_jerk_e = 5
|
||||||
|
machine_max_jerk_x = 8
|
||||||
|
machine_max_jerk_y = 8
|
||||||
|
machine_max_jerk_z = 0.4
|
||||||
|
machine_min_extruding_rate = 0
|
||||||
|
machine_min_travel_rate = 0
|
||||||
|
layer_gcode = ;AFTER_LAYER_CHANGE\n;[layer_z]
|
||||||
|
max_layer_height = 0.3
|
||||||
|
min_layer_height = 0.07
|
||||||
|
max_print_height = 250
|
||||||
|
nozzle_diameter = 0.4
|
||||||
|
printer_notes =
|
||||||
|
printer_settings_id =
|
||||||
|
retract_before_travel = 1
|
||||||
|
retract_before_wipe = 0%
|
||||||
|
retract_layer_change = 1
|
||||||
|
retract_length = 1
|
||||||
|
retract_length_toolchange = 1
|
||||||
|
retract_lift = 0
|
||||||
|
retract_lift_above = 0
|
||||||
|
retract_lift_below = 0
|
||||||
|
retract_restart_extra = 0
|
||||||
|
retract_restart_extra_toolchange = 0
|
||||||
|
retract_speed = 35
|
||||||
|
single_extruder_multi_material = 0
|
||||||
|
thumbnails = 16x16,220x124
|
||||||
|
toolchange_gcode =
|
||||||
|
use_firmware_retraction = 0
|
||||||
|
use_relative_e_distances = 1
|
||||||
|
use_volumetric_e = 0
|
||||||
|
variable_layer_height = 1
|
||||||
|
wipe = 1
|
||||||
|
z_offset = 0
|
||||||
|
printer_model =
|
||||||
|
default_print_profile = 0.16mm OPTIMAL @ELEGOO
|
||||||
|
default_filament_profile = Generic PLA @ELEGOO
|
||||||
|
|
||||||
|
[printer:Elegoo Neptune-2]
|
||||||
|
inherits = *common*
|
||||||
|
printer_model = NEPTUNE2
|
||||||
|
printer_variant = 0.4
|
||||||
|
max_layer_height = 0.28
|
||||||
|
min_layer_height = 0.08
|
||||||
|
printer_notes = Do not remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_ELEGOO\nPRINTER_MODEL_NEPTUNE2\nPRINTER_HAS_BOWDEN
|
||||||
|
max_print_height = 250
|
||||||
|
machine_max_acceleration_e = 5000
|
||||||
|
machine_max_acceleration_extruding = 500
|
||||||
|
machine_max_acceleration_retracting = 1000
|
||||||
|
machine_max_acceleration_x = 500
|
||||||
|
machine_max_acceleration_y = 500
|
||||||
|
machine_max_acceleration_z = 100
|
||||||
|
machine_max_feedrate_e = 60
|
||||||
|
machine_max_feedrate_x = 500
|
||||||
|
machine_max_feedrate_y = 500
|
||||||
|
machine_max_feedrate_z = 10
|
||||||
|
machine_max_jerk_e = 5
|
||||||
|
machine_max_jerk_x = 8
|
||||||
|
machine_max_jerk_y = 8
|
||||||
|
machine_max_jerk_z = 0.4
|
||||||
|
machine_min_extruding_rate = 0
|
||||||
|
machine_min_travel_rate = 0
|
||||||
|
nozzle_diameter = 0.4
|
||||||
|
retract_before_travel = 2
|
||||||
|
retract_length = 5
|
||||||
|
retract_speed = 60
|
||||||
|
deretract_speed = 40
|
||||||
|
retract_before_wipe = 70%
|
||||||
|
start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S120 ; set temporary nozzle temp to prevent oozing during homing and auto bed leveling\nM140 S[first_layer_bed_temperature] ; set final bed temp\nG4 S10 ; allow partial nozzle warmup\nG28 ; home all axis\nG1 Z50 F240\nG1 X2 Y10 F3000\nM104 S[first_layer_temperature] ; set final nozzle temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp to stabilize\nM109 S[first_layer_temperature] ; wait for nozzle temp to stabilize\nG1 Z0.28 F240\nG92 E0\nG1 Y140 E10 F1500 ; prime the nozzle\nG1 X2.3 F5000\nG92 E0\nG1 Y10 E10 F1200 ; prime the nozzle\nG92 E0
|
||||||
|
end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+2, max_print_height)} F600 ; Move print head up{endif}\nG1 X5 Y{print_bed_max[1]*0.8} F{travel_speed*60} ; present print\n{if max_layer_z < max_print_height-10}G1 Z{z_offset+min(max_layer_z+70, max_print_height-10)} F600 ; Move print head further up{endif}\n{if max_layer_z < max_print_height*0.6}G1 Z{max_print_height*0.6} F600 ; Move print head further up{endif}\nM140 S0 ; turn off heatbed\nM104 S0 ; turn off temperature\nM107 ; turn off fan\nM84 X Y E ; disable motors
|
||||||
|
|
||||||
|
|
||||||
|
# Intended for printers with a smaller bed
|
||||||
|
# [printer:*fastabl*]
|
||||||
|
# start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S120 ; set temporary nozzle temp to prevent oozing during homing and auto bed leveling\nM140 S[first_layer_bed_temperature] ; set final bed temp\nG4 S10 ; allow partial nozzle warmup\nG28 ; home all axis\nG29 ; auto bed levelling\nG1 Z50 F240\nG1 X2 Y10 F3000\nM104 S[first_layer_temperature] ; set final nozzle temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp to stabilize\nM109 S[first_layer_temperature] ; wait for nozzle temp to stabilize\nG1 Z0.28 F240\nG92 E0\nG1 Y140 E10 F1500 ; prime the nozzle\nG1 X2.3 F5000\nG92 E0\nG1 Y10 E10 F1200 ; prime the nozzle\nG92 E0
|
||||||
|
|
||||||
|
# Intended for printers with a larger bed
|
||||||
|
# [printer:*slowabl*]
|
||||||
|
# start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S120 ; set temporary nozzle temp to prevent oozing during homing and auto bed leveling\nM140 S[first_layer_bed_temperature] ; set final bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp to stabilize\nG28 ; home all axis\nG29 ; auto bed levelling\nG1 Z50 F240\nG1 X2 Y10 F3000\nM104 S[first_layer_temperature] ; set final nozzle temp\nM109 S[first_layer_temperature] ; wait for nozzle temp to stabilize\nG1 Z0.28 F240\nG92 E0\nG1 Y140 E10 F1500 ; prime the nozzle\nG1 X2.3 F5000\nG92 E0\nG1 Y10 E10 F1200 ; prime the nozzle\nG92 E0
|
||||||
|
|
||||||
|
# Intended for printers with vendor official firmware verified to support M25
|
||||||
|
# [printer:*pauseprint*]
|
||||||
|
# pause_print_gcode = M25 ; pause print
|
||||||
|
|
||||||
|
# Intended for printers where the Z-axis lowers the print bed during printing
|
||||||
|
# [printer:*invertedz*]
|
||||||
|
# end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+2, max_print_height)} F600{endif} ; Move print bed down\nG1 X50 Y50 F{travel_speed*60} ; present print\n{if max_layer_z < max_print_height-10}G1 Z{z_offset+max_print_height-10} F600{endif} ; Move print bed down further down\nM140 S0 ; turn off heatbed\nM104 S0 ; turn off temperature\nM107 ; turn off fan\nM84 X Y E ; disable motors
|
||||||
|
|
||||||
|
# Intended for printers with dual extruders and a single hotend/nozzle
|
||||||
|
[printer:*dualextruder*]
|
||||||
|
single_extruder_multi_material = 1
|
||||||
|
cooling_tube_length = 23
|
||||||
|
cooling_tube_retraction = 35
|
||||||
|
extra_loading_move = -2
|
||||||
|
parking_pos_retraction = 80
|
||||||
|
deretract_speed = 40,40
|
||||||
|
extruder_colour = #0080C0;#FFFF9F
|
||||||
|
extruder_offset = 0x0,0x0
|
||||||
|
max_layer_height = 0.28,0.28
|
||||||
|
min_layer_height = 0.08,0.08
|
||||||
|
nozzle_diameter = 0.4,0.4
|
||||||
|
retract_before_travel = 2,2
|
||||||
|
retract_before_wipe = 70%,70%
|
||||||
|
retract_layer_change = 1,1
|
||||||
|
retract_length = 5,5
|
||||||
|
retract_length_toolchange = 1,1
|
||||||
|
retract_lift = 0,0
|
||||||
|
retract_lift_above = 0,0
|
||||||
|
retract_lift_below = 0,0
|
||||||
|
retract_restart_extra = 0,0
|
||||||
|
retract_restart_extra_toolchange = 0,0
|
||||||
|
retract_speed = 60,60
|
||||||
|
wipe = 1,1
|
||||||
|
start_gcode = T[initial_tool] ; set active extruder\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM140 S{first_layer_bed_temperature[0]} ; set final bed temp\nM104 S150 ; set temporary nozzle temp to prevent oozing during homing and auto bed leveling\nG4 S10 ; allow partial nozzle warmup\nG28 ; home all axis\n;G29 ; auto bed levelling - remove ; at beginning of line to enable\n;M420 S1 ; enable mesh - remove ; at beginning of line to enable\nG1 Z50 F240\nG1 X2 Y10 F3000\nM104 S{first_layer_temperature[0]} ; set final nozzle temp\nM190 S{first_layer_bed_temperature[0]} ; wait for bed temp to stabilize\nM109 S{first_layer_temperature[0]} ; wait for nozzle temp to stabilize\nG1 Z0.28 F240 ; move down to prime nozzle\nG92 E0 ; reset extruder\nG1 E90 ; load filament\nG92 E0 ; reset extruder\nG1 Y140 E10 F1500 ; prime the nozzle\nG1 X2.3 F5000 ; move over for second prime line\nG92 E0 ; reset extruder\nG1 Y10 E10 F1200 ; prime the nozzle\nG92 E0 ; reset extruder
|
||||||
|
end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+2, max_print_height)} F600 ; Move print head up{endif}\nG1 X5 Y{print_bed_max[1]*0.8} F{travel_speed*60} ; present print\nG1 E-80 F2000 ; unload filament\n{if max_layer_z < max_print_height-10}G1 Z{z_offset+min(max_layer_z+70, max_print_height-10)} F600 ; Move print head further up{endif}\n{if max_layer_z < max_print_height*0.6}G1 Z{max_print_height*0.6} F600 ; Move print head further up{endif}\nM140 S0 ; turn off heatbed\nM104 S0 ; turn off temperature\nM107 ; turn off fan\nM84 X Y E ; disable motors
|
||||||
|
|
||||||
|
# Copy of Creality CR-X config for the Neptune 2D (dual extruder, single hotend)
|
||||||
|
|
||||||
|
[printer:Elegoo Neptune-2D]
|
||||||
|
inherits = Elegoo Neptune-2; *dualextruder*
|
||||||
|
retract_length = 6,6
|
||||||
|
printer_model = NEPTUNE2D
|
||||||
|
printer_notes = Do not remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_ELEGOO\nPRINTER_MODEL_NEPTUNE2D\nPRINTER_HAS_BOWDEN
|
||||||
|
|
||||||
|
[printer:Elegoo Neptune-2S]
|
||||||
|
inherits = Elegoo Neptune-2
|
||||||
|
printer_model = NEPTUNE2S
|
||||||
|
printer_notes = Do not remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_ELEGOO\nPRINTER_MODEL_NEPTUNE2D\nPRINTER_HAS_BOWDEN
|
||||||
|
|
||||||
|
[printer:Elegoo Neptune-X]
|
||||||
|
inherits = Elegoo Neptune-2
|
||||||
|
max_print_height = 300
|
||||||
|
printer_model = NEPTUNEX
|
||||||
|
printer_notes = Do not remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_ELEGOO\nPRINTER_MODEL_NEPTUNE2D\nPRINTER_HAS_BOWDEN
|
||||||
|
|
||||||
|
[printer:Elegoo Neptune-3]
|
||||||
|
inherits = Elegoo Neptune-2
|
||||||
|
max_print_height = 280
|
||||||
|
start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S120 ; set temporary nozzle temp to prevent oozing during homing and auto bed leveling\nM140 S[first_layer_bed_temperature] ; set final bed temp\nG4 S10 ; allow partial nozzle warmup\nG28 ; home all axis\nG29 ; run abl mesh\nM420 S1 ; load mesh\nG1 Z50 F240\nG1 X2 Y10 F3000\nM104 S[first_layer_temperature] ; set final nozzle temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp to stabilize\nM109 S[first_layer_temperature] ; wait for nozzle temp to stabilize\nG1 Z0.28 F240\nG92 E0\nG1 Y140 E10 F1500 ; prime the nozzle\nG1 X2.3 F5000\nG92 E0\nG1 Y10 E10 F1200 ; prime the nozzle\nG92 E0
|
||||||
|
printer_model = NEPTUNE3
|
||||||
|
printer_notes = Do not remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_ELEGOO\nPRINTER_MODEL_NEPTUNE2D\nPRINTER_HAS_BOWDEN
|
||||||
|
|
||||||
|
[printer:Elegoo Neptune-1]
|
||||||
|
inherits = Elegoo Neptune-2
|
||||||
|
bed_shape = 0x0,210x0,210x210,0x210
|
||||||
|
max_print_height = 200
|
||||||
|
printer_model = NEPTUNE1
|
||||||
|
printer_notes = Do not remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_ELEGOO\nPRINTER_MODEL_NEPTUNE2D\nPRINTER_HAS_BOWDEN
|
BIN
resources/profiles/Elegoo/NEPTUNE1_thumbnail.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
resources/profiles/Elegoo/NEPTUNE2D_thumbnail.png
Normal file
After Width: | Height: | Size: 45 KiB |
BIN
resources/profiles/Elegoo/NEPTUNE2S_thumbnail.png
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
resources/profiles/Elegoo/NEPTUNE2_thumbnail.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
resources/profiles/Elegoo/NEPTUNE3_thumbnail.png
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
resources/profiles/Elegoo/NEPTUNEX_thumbnail.png
Normal file
After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 10 KiB |
@ -14,8 +14,6 @@ add_subdirectory(semver)
|
|||||||
add_subdirectory(libigl)
|
add_subdirectory(libigl)
|
||||||
add_subdirectory(hints)
|
add_subdirectory(hints)
|
||||||
add_subdirectory(qoi)
|
add_subdirectory(qoi)
|
||||||
|
|
||||||
# Adding libnest2d project for bin packing...
|
|
||||||
add_subdirectory(libnest2d)
|
add_subdirectory(libnest2d)
|
||||||
|
|
||||||
find_package(Qhull 7.2 REQUIRED)
|
find_package(Qhull 7.2 REQUIRED)
|
||||||
@ -30,6 +28,10 @@ endif()
|
|||||||
|
|
||||||
add_subdirectory(libslic3r)
|
add_subdirectory(libslic3r)
|
||||||
|
|
||||||
|
if (SLIC3R_ENABLE_FORMAT_STEP)
|
||||||
|
add_subdirectory(occt_wrapper)
|
||||||
|
endif ()
|
||||||
|
|
||||||
if (SLIC3R_GUI)
|
if (SLIC3R_GUI)
|
||||||
add_subdirectory(imgui)
|
add_subdirectory(imgui)
|
||||||
add_subdirectory(hidapi)
|
add_subdirectory(hidapi)
|
||||||
@ -146,6 +148,7 @@ if (NOT WIN32 AND NOT APPLE)
|
|||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
target_link_libraries(PrusaSlicer libslic3r libcereal)
|
target_link_libraries(PrusaSlicer libslic3r libcereal)
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
# add_compile_options(-stdlib=libc++)
|
# add_compile_options(-stdlib=libc++)
|
||||||
# add_definitions(-DBOOST_THREAD_DONT_USE_CHRONO -DBOOST_NO_CXX11_RVALUE_REFERENCES -DBOOST_THREAD_USES_MOVE)
|
# add_definitions(-DBOOST_THREAD_DONT_USE_CHRONO -DBOOST_NO_CXX11_RVALUE_REFERENCES -DBOOST_THREAD_USES_MOVE)
|
||||||
|
@ -21,7 +21,7 @@ DistributedBeadingStrategy::DistributedBeadingStrategy(const coord_t optimal_wid
|
|||||||
name = "DistributedBeadingStrategy";
|
name = "DistributedBeadingStrategy";
|
||||||
}
|
}
|
||||||
|
|
||||||
DistributedBeadingStrategy::Beading DistributedBeadingStrategy::compute(coord_t thickness, coord_t bead_count) const
|
DistributedBeadingStrategy::Beading DistributedBeadingStrategy::compute(const coord_t thickness, const coord_t bead_count) const
|
||||||
{
|
{
|
||||||
Beading ret;
|
Beading ret;
|
||||||
|
|
||||||
@ -40,18 +40,24 @@ DistributedBeadingStrategy::Beading DistributedBeadingStrategy::compute(coord_t
|
|||||||
for (coord_t bead_idx = 0; bead_idx < bead_count; bead_idx++)
|
for (coord_t bead_idx = 0; bead_idx < bead_count; bead_idx++)
|
||||||
weights[bead_idx] = getWeight(bead_idx);
|
weights[bead_idx] = getWeight(bead_idx);
|
||||||
|
|
||||||
const float total_weight = std::accumulate(weights.cbegin(), weights.cend(), 0.f);
|
const float total_weight = std::accumulate(weights.cbegin(), weights.cend(), 0.f);
|
||||||
|
coord_t accumulated_width = 0;
|
||||||
for (coord_t bead_idx = 0; bead_idx < bead_count; bead_idx++) {
|
for (coord_t bead_idx = 0; bead_idx < bead_count; bead_idx++) {
|
||||||
const float weight_fraction = weights[bead_idx] / total_weight;
|
const float weight_fraction = weights[bead_idx] / total_weight;
|
||||||
const coord_t splitup_left_over_weight = to_be_divided * weight_fraction;
|
const coord_t splitup_left_over_weight = to_be_divided * weight_fraction;
|
||||||
const coord_t width = optimal_width + splitup_left_over_weight;
|
const coord_t width = (bead_idx == bead_count - 1) ? thickness - accumulated_width : optimal_width + splitup_left_over_weight;
|
||||||
|
|
||||||
|
// Be aware that toolpath_locations is computed by dividing the width by 2, so toolpath_locations
|
||||||
|
// could be off by 1 because of rounding errors.
|
||||||
if (bead_idx == 0)
|
if (bead_idx == 0)
|
||||||
ret.toolpath_locations.emplace_back(width / 2);
|
ret.toolpath_locations.emplace_back(width / 2);
|
||||||
else
|
else
|
||||||
ret.toolpath_locations.emplace_back(ret.toolpath_locations.back() + (ret.bead_widths.back() + width) / 2);
|
ret.toolpath_locations.emplace_back(ret.toolpath_locations.back() + (ret.bead_widths.back() + width) / 2);
|
||||||
ret.bead_widths.emplace_back(width);
|
ret.bead_widths.emplace_back(width);
|
||||||
|
accumulated_width += width;
|
||||||
}
|
}
|
||||||
ret.left_over = 0;
|
ret.left_over = 0;
|
||||||
|
assert((accumulated_width + ret.left_over) == thickness);
|
||||||
} else if (bead_count == 2) {
|
} else if (bead_count == 2) {
|
||||||
const coord_t outer_width = thickness / 2;
|
const coord_t outer_width = thickness / 2;
|
||||||
ret.bead_widths.emplace_back(outer_width);
|
ret.bead_widths.emplace_back(outer_width);
|
||||||
@ -68,6 +74,13 @@ DistributedBeadingStrategy::Beading DistributedBeadingStrategy::compute(coord_t
|
|||||||
ret.left_over = thickness;
|
ret.left_over = thickness;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(([&ret = std::as_const(ret), thickness]() -> bool {
|
||||||
|
coord_t total_bead_width = 0;
|
||||||
|
for (const coord_t &bead_width : ret.bead_widths)
|
||||||
|
total_bead_width += bead_width;
|
||||||
|
return (total_bead_width + ret.left_over) == thickness;
|
||||||
|
}()));
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "SVG.hpp"
|
#include "SVG.hpp"
|
||||||
#include "Geometry/VoronoiVisualUtils.hpp"
|
#include "Geometry/VoronoiVisualUtils.hpp"
|
||||||
|
#include "Geometry/VoronoiUtilsCgal.hpp"
|
||||||
#include "../EdgeGrid.hpp"
|
#include "../EdgeGrid.hpp"
|
||||||
|
|
||||||
#define SKELETAL_TRAPEZOIDATION_BEAD_SEARCH_MAX 1000 //A limit to how long it'll keep searching for adjacent beads. Increasing will re-use beadings more often (saving performance), but search longer for beading (costing performance).
|
#define SKELETAL_TRAPEZOIDATION_BEAD_SEARCH_MAX 1000 //A limit to how long it'll keep searching for adjacent beads. Increasing will re-use beadings more often (saving performance), but search longer for beading (costing performance).
|
||||||
@ -43,6 +44,71 @@ template<> struct segment_traits<Slic3r::Arachne::PolygonsSegmentIndex>
|
|||||||
namespace Slic3r::Arachne
|
namespace Slic3r::Arachne
|
||||||
{
|
{
|
||||||
|
|
||||||
|
#ifdef ARACHNE_DEBUG
|
||||||
|
static void export_graph_to_svg(const std::string &path,
|
||||||
|
SkeletalTrapezoidationGraph &graph,
|
||||||
|
const Polygons &polys,
|
||||||
|
const std::vector<std::shared_ptr<LineJunctions>> &edge_junctions = {},
|
||||||
|
const bool beat_count = true,
|
||||||
|
const bool transition_middles = true,
|
||||||
|
const bool transition_ends = true)
|
||||||
|
{
|
||||||
|
const std::vector<std::string> colors = {"blue", "cyan", "red", "orange", "magenta", "pink", "purple", "green", "yellow"};
|
||||||
|
coordf_t stroke_width = scale_(0.03);
|
||||||
|
BoundingBox bbox = get_extents(polys);
|
||||||
|
for (const auto &node : graph.nodes)
|
||||||
|
bbox.merge(node.p);
|
||||||
|
|
||||||
|
bbox.offset(scale_(1.));
|
||||||
|
|
||||||
|
::Slic3r::SVG svg(path.c_str(), bbox);
|
||||||
|
for (const auto &line : to_lines(polys))
|
||||||
|
svg.draw(line, "gray", stroke_width);
|
||||||
|
|
||||||
|
for (const auto &edge : graph.edges)
|
||||||
|
svg.draw(Line(edge.from->p, edge.to->p), (edge.data.centralIsSet() && edge.data.isCentral()) ? "blue" : "cyan", stroke_width);
|
||||||
|
|
||||||
|
for (const auto &line_junction : edge_junctions)
|
||||||
|
for (const auto &extrusion_junction : *line_junction)
|
||||||
|
svg.draw(extrusion_junction.p, "orange", coord_t(stroke_width * 2.));
|
||||||
|
|
||||||
|
if (beat_count) {
|
||||||
|
for (const auto &node : graph.nodes) {
|
||||||
|
svg.draw(node.p, "red", coord_t(stroke_width * 1.6));
|
||||||
|
svg.draw_text(node.p, std::to_string(node.data.bead_count).c_str(), "black", 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transition_middles) {
|
||||||
|
for (auto &edge : graph.edges) {
|
||||||
|
if (std::shared_ptr<std::list<SkeletalTrapezoidationEdge::TransitionMiddle>> transitions = edge.data.getTransitions(); transitions) {
|
||||||
|
for (auto &transition : *transitions) {
|
||||||
|
Line edge_line = Line(edge.to->p, edge.from->p);
|
||||||
|
double edge_length = edge_line.length();
|
||||||
|
Point pt = edge_line.a + (edge_line.vector().cast<double>() * (double(transition.pos) / edge_length)).cast<coord_t>();
|
||||||
|
svg.draw(pt, "magenta", coord_t(stroke_width * 1.5));
|
||||||
|
svg.draw_text(pt, std::to_string(transition.lower_bead_count).c_str(), "black", 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transition_ends) {
|
||||||
|
for (auto &edge : graph.edges) {
|
||||||
|
if (std::shared_ptr<std::list<SkeletalTrapezoidationEdge::TransitionEnd>> transitions = edge.data.getTransitionEnds(); transitions) {
|
||||||
|
for (auto &transition : *transitions) {
|
||||||
|
Line edge_line = Line(edge.to->p, edge.from->p);
|
||||||
|
double edge_length = edge_line.length();
|
||||||
|
Point pt = edge_line.a + (edge_line.vector().cast<double>() * (double(transition.pos) / edge_length)).cast<coord_t>();
|
||||||
|
svg.draw(pt, transition.is_lower_end ? "green" : "lime", coord_t(stroke_width * 1.5));
|
||||||
|
svg.draw_text(pt, std::to_string(transition.lower_bead_count).c_str(), "black", 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
SkeletalTrapezoidation::node_t& SkeletalTrapezoidation::makeNode(vd_t::vertex_type& vd_node, Point p)
|
SkeletalTrapezoidation::node_t& SkeletalTrapezoidation::makeNode(vd_t::vertex_type& vd_node, Point p)
|
||||||
{
|
{
|
||||||
auto he_node_it = vd_node_to_he_node.find(&vd_node);
|
auto he_node_it = vd_node_to_he_node.find(&vd_node);
|
||||||
@ -285,7 +351,6 @@ std::vector<Point> SkeletalTrapezoidation::discretize(const vd_t::edge_type& vd_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool SkeletalTrapezoidation::computePointCellRange(vd_t::cell_type& cell, Point& start_source_point, Point& end_source_point, vd_t::edge_type*& starting_vd_edge, vd_t::edge_type*& ending_vd_edge, const std::vector<Segment>& segments)
|
bool SkeletalTrapezoidation::computePointCellRange(vd_t::cell_type& cell, Point& start_source_point, Point& end_source_point, vd_t::edge_type*& starting_vd_edge, vd_t::edge_type*& ending_vd_edge, const std::vector<Segment>& segments)
|
||||||
{
|
{
|
||||||
if (cell.incident_edge()->is_infinite())
|
if (cell.incident_edge()->is_infinite())
|
||||||
@ -386,7 +451,8 @@ SkeletalTrapezoidation::SkeletalTrapezoidation(const Polygons& polys, const Bead
|
|||||||
constructFromPolygons(polys);
|
constructFromPolygons(polys);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool detect_missing_voronoi_vertex(const Geometry::VoronoiDiagram &voronoi_diagram, const std::vector<SkeletalTrapezoidation::Segment> &segments) {
|
|
||||||
|
static bool detect_missing_voronoi_vertex(const Geometry::VoronoiDiagram &voronoi_diagram, const std::vector<SkeletalTrapezoidation::Segment> &segments) {
|
||||||
for (VoronoiUtils::vd_t::cell_type cell : voronoi_diagram.cells()) {
|
for (VoronoiUtils::vd_t::cell_type cell : voronoi_diagram.cells()) {
|
||||||
if (!cell.incident_edge())
|
if (!cell.incident_edge())
|
||||||
continue; // There is no spoon
|
continue; // There is no spoon
|
||||||
@ -432,8 +498,64 @@ bool detect_missing_voronoi_vertex(const Geometry::VoronoiDiagram &voronoi_diagr
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool has_missing_twin_edge(const SkeletalTrapezoidationGraph &graph)
|
||||||
|
{
|
||||||
|
for (const auto &edge : graph.edges)
|
||||||
|
if (edge.twin == nullptr)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static std::unordered_map<Point, Point, PointHash> try_to_fix_degenerated_voronoi_diagram_by_rotation(
|
||||||
|
Geometry::VoronoiDiagram &voronoi_diagram,
|
||||||
|
const Polygons &polys,
|
||||||
|
Polygons &polys_copy,
|
||||||
|
std::vector<SkeletalTrapezoidation::Segment> &segments,
|
||||||
|
const double fix_angle)
|
||||||
|
{
|
||||||
|
std::unordered_map<Point, Point, PointHash> vertex_mapping;
|
||||||
|
for (Polygon &poly : polys_copy)
|
||||||
|
poly.rotate(fix_angle);
|
||||||
|
|
||||||
|
assert(polys_copy.size() == polys.size());
|
||||||
|
for (size_t poly_idx = 0; poly_idx < polys.size(); ++poly_idx) {
|
||||||
|
assert(polys_copy[poly_idx].size() == polys[poly_idx].size());
|
||||||
|
for (size_t point_idx = 0; point_idx < polys[poly_idx].size(); ++point_idx)
|
||||||
|
vertex_mapping.insert({polys[poly_idx][point_idx], polys_copy[poly_idx][point_idx]});
|
||||||
|
}
|
||||||
|
|
||||||
|
segments.clear();
|
||||||
|
for (size_t poly_idx = 0; poly_idx < polys_copy.size(); poly_idx++)
|
||||||
|
for (size_t point_idx = 0; point_idx < polys_copy[poly_idx].size(); point_idx++)
|
||||||
|
segments.emplace_back(&polys_copy, poly_idx, point_idx);
|
||||||
|
|
||||||
|
voronoi_diagram.clear();
|
||||||
|
construct_voronoi(segments.begin(), segments.end(), &voronoi_diagram);
|
||||||
|
|
||||||
|
assert(Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(voronoi_diagram));
|
||||||
|
|
||||||
|
return vertex_mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
for (STHalfEdgeNode &node : graph.nodes) {
|
||||||
|
// If a mapping exists between a rotated point and an original point, use this mapping. Otherwise, rotate a point in the opposite direction.
|
||||||
|
if (auto node_it = vertex_mapping.find(node.p); node_it != vertex_mapping.end())
|
||||||
|
node.p = node_it->second;
|
||||||
|
else
|
||||||
|
node.p.rotate(-fix_angle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys)
|
void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys)
|
||||||
{
|
{
|
||||||
|
#ifdef ARACHNE_DEBUG
|
||||||
|
this->outline = polys;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Check self intersections.
|
// Check self intersections.
|
||||||
assert([&polys]() -> bool {
|
assert([&polys]() -> bool {
|
||||||
EdgeGrid::Grid grid;
|
EdgeGrid::Grid grid;
|
||||||
@ -450,39 +572,61 @@ void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys)
|
|||||||
for (size_t point_idx = 0; point_idx < polys[poly_idx].size(); point_idx++)
|
for (size_t point_idx = 0; point_idx < polys[poly_idx].size(); point_idx++)
|
||||||
segments.emplace_back(&polys, poly_idx, point_idx);
|
segments.emplace_back(&polys, poly_idx, point_idx);
|
||||||
|
|
||||||
|
#ifdef ARACHNE_DEBUG
|
||||||
|
{
|
||||||
|
static int iRun = 0;
|
||||||
|
BoundingBox bbox = get_extents(polys);
|
||||||
|
SVG svg(debug_out_path("arachne_voronoi-input-%d.svg", iRun++).c_str(), bbox);
|
||||||
|
svg.draw_outline(polys, "black", scaled<coordf_t>(0.03f));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
Geometry::VoronoiDiagram voronoi_diagram;
|
Geometry::VoronoiDiagram voronoi_diagram;
|
||||||
construct_voronoi(segments.begin(), segments.end(), &voronoi_diagram);
|
construct_voronoi(segments.begin(), segments.end(), &voronoi_diagram);
|
||||||
|
|
||||||
// Try to detect cases when some Voronoi vertex is missing.
|
#ifdef ARACHNE_DEBUG_VORONOI
|
||||||
// When any Voronoi vertex is missing, rotate input polygon and try again.
|
{
|
||||||
const bool has_missing_voronoi_vertex = detect_missing_voronoi_vertex(voronoi_diagram, segments);
|
static int iRun = 0;
|
||||||
const double fix_angle = PI / 6;
|
dump_voronoi_to_svg(debug_out_path("arachne_voronoi-diagram-%d.svg", iRun++).c_str(), voronoi_diagram, to_points(polys), to_lines(polys));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ARACHNE_DEBUG
|
||||||
|
assert(Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(voronoi_diagram));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Try to detect cases when some Voronoi vertex is missing and when
|
||||||
|
// the Voronoi diagram is not planar.
|
||||||
|
// When any Voronoi vertex is missing, or the Voronoi diagram is not
|
||||||
|
// planar, rotate the input polygon and try again.
|
||||||
|
const bool has_missing_voronoi_vertex = detect_missing_voronoi_vertex(voronoi_diagram, segments);
|
||||||
|
// Detection of non-planar Voronoi diagram detects at least GH issues #8474, #8514 and #8446.
|
||||||
|
const bool is_voronoi_diagram_planar = Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(voronoi_diagram);
|
||||||
|
const double fix_angle = PI / 6;
|
||||||
|
|
||||||
std::unordered_map<Point, Point, PointHash> vertex_mapping;
|
std::unordered_map<Point, Point, PointHash> vertex_mapping;
|
||||||
|
// polys_copy is referenced through items stored in the std::vector segments.
|
||||||
Polygons polys_copy = polys;
|
Polygons polys_copy = polys;
|
||||||
if (has_missing_voronoi_vertex) {
|
if (has_missing_voronoi_vertex || !is_voronoi_diagram_planar) {
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Detected missing Voronoi vertex, input polygons will be rotated back and forth.";
|
if (has_missing_voronoi_vertex)
|
||||||
for (Polygon &poly : polys_copy)
|
BOOST_LOG_TRIVIAL(warning) << "Detected missing Voronoi vertex, input polygons will be rotated back and forth.";
|
||||||
poly.rotate(fix_angle);
|
else if (!is_voronoi_diagram_planar)
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "Detected non-planar Voronoi diagram, input polygons will be rotated back and forth.";
|
||||||
|
|
||||||
assert(polys_copy.size() == polys.size());
|
vertex_mapping = try_to_fix_degenerated_voronoi_diagram_by_rotation(voronoi_diagram, polys, polys_copy, segments, fix_angle);
|
||||||
for (size_t poly_idx = 0; poly_idx < polys.size(); ++poly_idx) {
|
|
||||||
assert(polys_copy[poly_idx].size() == polys[poly_idx].size());
|
|
||||||
for (size_t point_idx = 0; point_idx < polys[poly_idx].size(); ++point_idx)
|
|
||||||
vertex_mapping.insert({polys[poly_idx][point_idx], polys_copy[poly_idx][point_idx]});
|
|
||||||
}
|
|
||||||
|
|
||||||
segments.clear();
|
|
||||||
for (size_t poly_idx = 0; poly_idx < polys_copy.size(); poly_idx++)
|
|
||||||
for (size_t point_idx = 0; point_idx < polys_copy[poly_idx].size(); point_idx++)
|
|
||||||
segments.emplace_back(&polys_copy, poly_idx, point_idx);
|
|
||||||
|
|
||||||
voronoi_diagram.clear();
|
|
||||||
construct_voronoi(segments.begin(), segments.end(), &voronoi_diagram);
|
|
||||||
assert(!detect_missing_voronoi_vertex(voronoi_diagram, segments));
|
assert(!detect_missing_voronoi_vertex(voronoi_diagram, segments));
|
||||||
|
assert(Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(voronoi_diagram));
|
||||||
if (detect_missing_voronoi_vertex(voronoi_diagram, segments))
|
if (detect_missing_voronoi_vertex(voronoi_diagram, segments))
|
||||||
BOOST_LOG_TRIVIAL(error) << "Detected missing Voronoi vertex even after the rotation of input.";
|
BOOST_LOG_TRIVIAL(error) << "Detected missing Voronoi vertex even after the rotation of input.";
|
||||||
|
else if (!Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(voronoi_diagram))
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Detected non-planar Voronoi diagram even after the rotation of input.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool degenerated_voronoi_diagram = has_missing_voronoi_vertex || !is_voronoi_diagram_planar;
|
||||||
|
|
||||||
|
process_voronoi_diagram:
|
||||||
|
assert(this->graph.edges.empty() && this->graph.nodes.empty() && this->vd_edge_to_he_edge.empty() && this->vd_node_to_he_node.empty());
|
||||||
for (vd_t::cell_type cell : voronoi_diagram.cells()) {
|
for (vd_t::cell_type cell : voronoi_diagram.cells()) {
|
||||||
if (!cell.incident_edge())
|
if (!cell.incident_edge())
|
||||||
continue; // There is no spoon
|
continue; // There is no spoon
|
||||||
@ -538,16 +682,39 @@ void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys)
|
|||||||
prev_edge->to->data.distance_to_boundary = 0;
|
prev_edge->to->data.distance_to_boundary = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_missing_voronoi_vertex) {
|
// For some input polygons, as in GH issues #8474 and #8514 resulting Voronoi diagram is degenerated because it is not planar.
|
||||||
for (node_t &node : graph.nodes) {
|
// When this degenerated Voronoi diagram is processed, the resulting half-edge structure contains some edges that don't have
|
||||||
// If a mapping exists between a rotated point and an original point, use this mapping. Otherwise, rotate a point in the opposite direction.
|
// a twin edge. Based on this, we created a fast mechanism that detects those causes and tries to recompute the Voronoi
|
||||||
if (auto node_it = vertex_mapping.find(node.p); node_it != vertex_mapping.end())
|
// diagram on slightly rotated input polygons that usually make the Voronoi generator generate a non-degenerated Voronoi diagram.
|
||||||
node.p = node_it->second;
|
if (!degenerated_voronoi_diagram && has_missing_twin_edge(this->graph)) {
|
||||||
else
|
BOOST_LOG_TRIVIAL(warning) << "Detected degenerated Voronoi diagram, input polygons will be rotated back and forth.";
|
||||||
node.p.rotate(-fix_angle);
|
degenerated_voronoi_diagram = true;
|
||||||
}
|
vertex_mapping = try_to_fix_degenerated_voronoi_diagram_by_rotation(voronoi_diagram, polys, polys_copy, segments, fix_angle);
|
||||||
|
|
||||||
|
assert(!detect_missing_voronoi_vertex(voronoi_diagram, segments));
|
||||||
|
if (detect_missing_voronoi_vertex(voronoi_diagram, segments))
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Detected missing Voronoi vertex after the rotation of input.";
|
||||||
|
|
||||||
|
assert(Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(voronoi_diagram));
|
||||||
|
|
||||||
|
this->graph.edges.clear();
|
||||||
|
this->graph.nodes.clear();
|
||||||
|
this->vd_edge_to_he_edge.clear();
|
||||||
|
this->vd_node_to_he_node.clear();
|
||||||
|
|
||||||
|
goto process_voronoi_diagram;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (degenerated_voronoi_diagram) {
|
||||||
|
assert(!has_missing_twin_edge(this->graph));
|
||||||
|
|
||||||
|
if (has_missing_twin_edge(this->graph))
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Detected degenerated Voronoi diagram even after the rotation of input.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (degenerated_voronoi_diagram)
|
||||||
|
rotate_back_skeletal_trapezoidation_graph_after_fix(this->graph, fix_angle, vertex_mapping);
|
||||||
|
|
||||||
separatePointyQuadEndNodes();
|
separatePointyQuadEndNodes();
|
||||||
|
|
||||||
graph.collapseSmallEdges();
|
graph.collapseSmallEdges();
|
||||||
@ -594,45 +761,62 @@ void SkeletalTrapezoidation::separatePointyQuadEndNodes()
|
|||||||
// vvvvvvvvvvvvvvvvvvvvv
|
// vvvvvvvvvvvvvvvvvvvvv
|
||||||
//
|
//
|
||||||
|
|
||||||
#if 0
|
|
||||||
static void export_graph_to_svg(const std::string &path, const SkeletalTrapezoidationGraph &graph, const Polygons &polys)
|
|
||||||
{
|
|
||||||
const std::vector<std::string> colors = {"blue", "cyan", "red", "orange", "magenta", "pink", "purple", "green", "yellow"};
|
|
||||||
coordf_t stroke_width = scale_(0.05);
|
|
||||||
BoundingBox bbox;
|
|
||||||
for (const auto &node : graph.nodes)
|
|
||||||
bbox.merge(node.p);
|
|
||||||
|
|
||||||
bbox.offset(scale_(1.));
|
|
||||||
::Slic3r::SVG svg(path.c_str(), bbox);
|
|
||||||
for (const auto &line : to_lines(polys))
|
|
||||||
svg.draw(line, "red", stroke_width);
|
|
||||||
|
|
||||||
for (const auto &edge : graph.edges)
|
|
||||||
svg.draw(Line(edge.from->p, edge.to->p), "cyan", scale_(0.01));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void SkeletalTrapezoidation::generateToolpaths(std::vector<VariableWidthLines> &generated_toolpaths, bool filter_outermost_central_edges)
|
void SkeletalTrapezoidation::generateToolpaths(std::vector<VariableWidthLines> &generated_toolpaths, bool filter_outermost_central_edges)
|
||||||
{
|
{
|
||||||
|
#ifdef ARACHNE_DEBUG
|
||||||
|
static int iRun = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
p_generated_toolpaths = &generated_toolpaths;
|
p_generated_toolpaths = &generated_toolpaths;
|
||||||
|
|
||||||
updateIsCentral();
|
updateIsCentral();
|
||||||
|
|
||||||
|
#ifdef ARACHNE_DEBUG
|
||||||
|
export_graph_to_svg(debug_out_path("ST-updateIsCentral-final-%d.svg", iRun), this->graph, this->outline);
|
||||||
|
#endif
|
||||||
|
|
||||||
filterCentral(central_filter_dist);
|
filterCentral(central_filter_dist);
|
||||||
|
|
||||||
|
#ifdef ARACHNE_DEBUG
|
||||||
|
export_graph_to_svg(debug_out_path("ST-filterCentral-final-%d.svg", iRun), this->graph, this->outline);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (filter_outermost_central_edges)
|
if (filter_outermost_central_edges)
|
||||||
filterOuterCentral();
|
filterOuterCentral();
|
||||||
|
|
||||||
updateBeadCount();
|
updateBeadCount();
|
||||||
|
|
||||||
|
#ifdef ARACHNE_DEBUG
|
||||||
|
export_graph_to_svg(debug_out_path("ST-updateBeadCount-final-%d.svg", iRun), this->graph, this->outline);
|
||||||
|
#endif
|
||||||
|
|
||||||
filterNoncentralRegions();
|
filterNoncentralRegions();
|
||||||
|
|
||||||
|
#ifdef ARACHNE_DEBUG
|
||||||
|
export_graph_to_svg(debug_out_path("ST-filterNoncentralRegions-final-%d.svg", iRun), this->graph, this->outline);
|
||||||
|
#endif
|
||||||
|
|
||||||
generateTransitioningRibs();
|
generateTransitioningRibs();
|
||||||
|
|
||||||
|
#ifdef ARACHNE_DEBUG
|
||||||
|
export_graph_to_svg(debug_out_path("ST-generateTransitioningRibs-final-%d.svg", iRun), this->graph, this->outline);
|
||||||
|
#endif
|
||||||
|
|
||||||
generateExtraRibs();
|
generateExtraRibs();
|
||||||
|
|
||||||
|
#ifdef ARACHNE_DEBUG
|
||||||
|
export_graph_to_svg(debug_out_path("ST-generateExtraRibs-final-%d.svg", iRun), this->graph, this->outline);
|
||||||
|
#endif
|
||||||
|
|
||||||
generateSegments();
|
generateSegments();
|
||||||
|
|
||||||
|
#ifdef ARACHNE_DEBUG
|
||||||
|
export_graph_to_svg(debug_out_path("ST-generateSegments-final-%d.svg", iRun), this->graph, this->outline);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ARACHNE_DEBUG
|
||||||
|
++iRun;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkeletalTrapezoidation::updateIsCentral()
|
void SkeletalTrapezoidation::updateIsCentral()
|
||||||
@ -844,11 +1028,24 @@ void SkeletalTrapezoidation::generateTransitioningRibs()
|
|||||||
|
|
||||||
filterTransitionMids();
|
filterTransitionMids();
|
||||||
|
|
||||||
|
#ifdef ARACHNE_DEBUG
|
||||||
|
static int iRun = 0;
|
||||||
|
export_graph_to_svg(debug_out_path("ST-generateTransitioningRibs-mids-%d.svg", iRun++), this->graph, this->outline);
|
||||||
|
#endif
|
||||||
|
|
||||||
ptr_vector_t<std::list<TransitionEnd>> edge_transition_ends; // We only map the half edge in the upward direction. mapped items are not sorted
|
ptr_vector_t<std::list<TransitionEnd>> edge_transition_ends; // We only map the half edge in the upward direction. mapped items are not sorted
|
||||||
generateAllTransitionEnds(edge_transition_ends);
|
generateAllTransitionEnds(edge_transition_ends);
|
||||||
|
|
||||||
|
#ifdef ARACHNE_DEBUG
|
||||||
|
export_graph_to_svg(debug_out_path("ST-generateTransitioningRibs-ends-%d.svg", iRun++), this->graph, this->outline);
|
||||||
|
#endif
|
||||||
|
|
||||||
applyTransitions(edge_transition_ends);
|
applyTransitions(edge_transition_ends);
|
||||||
// Note that the shared pointer lists will be out of scope and thus destroyed here, since the remaining refs are weak_ptr.
|
// Note that the shared pointer lists will be out of scope and thus destroyed here, since the remaining refs are weak_ptr.
|
||||||
|
|
||||||
|
#ifdef ARACHNE_DEBUG
|
||||||
|
++iRun;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1569,16 +1766,37 @@ void SkeletalTrapezoidation::generateSegments()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ARACHNE_DEBUG
|
||||||
|
static int iRun = 0;
|
||||||
|
export_graph_to_svg(debug_out_path("ST-generateSegments-before-propagation-%d.svg", iRun), this->graph, this->outline);
|
||||||
|
#endif
|
||||||
|
|
||||||
propagateBeadingsUpward(upward_quad_mids, node_beadings);
|
propagateBeadingsUpward(upward_quad_mids, node_beadings);
|
||||||
|
|
||||||
|
#ifdef ARACHNE_DEBUG
|
||||||
|
export_graph_to_svg(debug_out_path("ST-generateSegments-upward-propagation-%d.svg", iRun), this->graph, this->outline);
|
||||||
|
#endif
|
||||||
|
|
||||||
propagateBeadingsDownward(upward_quad_mids, node_beadings);
|
propagateBeadingsDownward(upward_quad_mids, node_beadings);
|
||||||
|
|
||||||
|
#ifdef ARACHNE_DEBUG
|
||||||
|
export_graph_to_svg(debug_out_path("ST-generateSegments-downward-propagation-%d.svg", iRun), this->graph, this->outline);
|
||||||
|
#endif
|
||||||
|
|
||||||
ptr_vector_t<LineJunctions> edge_junctions; // junctions ordered high R to low R
|
ptr_vector_t<LineJunctions> edge_junctions; // junctions ordered high R to low R
|
||||||
generateJunctions(node_beadings, edge_junctions);
|
generateJunctions(node_beadings, edge_junctions);
|
||||||
|
|
||||||
|
#ifdef ARACHNE_DEBUG
|
||||||
|
export_graph_to_svg(debug_out_path("ST-generateSegments-junctions-%d.svg", iRun), this->graph, this->outline, edge_junctions);
|
||||||
|
#endif
|
||||||
|
|
||||||
connectJunctions(edge_junctions);
|
connectJunctions(edge_junctions);
|
||||||
|
|
||||||
generateLocalMaximaSingleBeads();
|
generateLocalMaximaSingleBeads();
|
||||||
|
|
||||||
|
#ifdef ARACHNE_DEBUG
|
||||||
|
++iRun;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
SkeletalTrapezoidation::edge_t* SkeletalTrapezoidation::getQuadMaxRedgeTo(edge_t* quad_start_edge)
|
SkeletalTrapezoidation::edge_t* SkeletalTrapezoidation::getQuadMaxRedgeTo(edge_t* quad_start_edge)
|
||||||
@ -1811,7 +2029,10 @@ void SkeletalTrapezoidation::generateJunctions(ptr_vector_t<BeadingPropagation>&
|
|||||||
for (junction_idx = (std::max(size_t(1), beading->toolpath_locations.size()) - 1) / 2; junction_idx < num_junctions; junction_idx--)
|
for (junction_idx = (std::max(size_t(1), beading->toolpath_locations.size()) - 1) / 2; junction_idx < num_junctions; junction_idx--)
|
||||||
{
|
{
|
||||||
coord_t bead_R = beading->toolpath_locations[junction_idx];
|
coord_t bead_R = beading->toolpath_locations[junction_idx];
|
||||||
if (bead_R <= start_R)
|
// toolpath_locations computed inside DistributedBeadingStrategy could be off by 1 because of rounding errors.
|
||||||
|
// In GH issue #8472, these roundings errors caused missing the middle extrusion.
|
||||||
|
// Adding small epsilon should help resolve those cases.
|
||||||
|
if (bead_R <= start_R + 1)
|
||||||
{ // Junction coinciding with start node is used in this function call
|
{ // Junction coinciding with start node is used in this function call
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,10 @@
|
|||||||
#include "SkeletalTrapezoidationJoint.hpp"
|
#include "SkeletalTrapezoidationJoint.hpp"
|
||||||
#include "libslic3r/Arachne/BeadingStrategy/BeadingStrategy.hpp"
|
#include "libslic3r/Arachne/BeadingStrategy/BeadingStrategy.hpp"
|
||||||
#include "SkeletalTrapezoidationGraph.hpp"
|
#include "SkeletalTrapezoidationGraph.hpp"
|
||||||
|
#include "../Geometry/Voronoi.hpp"
|
||||||
|
|
||||||
|
//#define ARACHNE_DEBUG
|
||||||
|
//#define ARACHNE_DEBUG_VORONOI
|
||||||
|
|
||||||
namespace Slic3r::Arachne
|
namespace Slic3r::Arachne
|
||||||
{
|
{
|
||||||
@ -122,6 +126,10 @@ public:
|
|||||||
*/
|
*/
|
||||||
void generateToolpaths(std::vector<VariableWidthLines> &generated_toolpaths, bool filter_outermost_central_edges = false);
|
void generateToolpaths(std::vector<VariableWidthLines> &generated_toolpaths, bool filter_outermost_central_edges = false);
|
||||||
|
|
||||||
|
#ifdef ARACHNE_DEBUG
|
||||||
|
Polygons outline;
|
||||||
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/*!
|
/*!
|
||||||
* Auxiliary for referencing one transition along an edge which may contain multiple transitions
|
* Auxiliary for referencing one transition along an edge which may contain multiple transitions
|
||||||
|
@ -24,30 +24,37 @@ namespace Slic3r::Arachne
|
|||||||
{
|
{
|
||||||
|
|
||||||
WallToolPaths::WallToolPaths(const Polygons& outline, const coord_t bead_width_0, const coord_t bead_width_x,
|
WallToolPaths::WallToolPaths(const Polygons& outline, const coord_t bead_width_0, const coord_t bead_width_x,
|
||||||
const size_t inset_count, const coord_t wall_0_inset, const PrintObjectConfig &print_object_config, const PrintConfig &print_config)
|
const size_t inset_count, const coord_t wall_0_inset, const coordf_t layer_height,
|
||||||
|
const PrintObjectConfig &print_object_config, const PrintConfig &print_config)
|
||||||
: outline(outline)
|
: outline(outline)
|
||||||
, bead_width_0(bead_width_0)
|
, bead_width_0(bead_width_0)
|
||||||
, bead_width_x(bead_width_x)
|
, bead_width_x(bead_width_x)
|
||||||
, inset_count(inset_count)
|
, inset_count(inset_count)
|
||||||
, wall_0_inset(wall_0_inset)
|
, wall_0_inset(wall_0_inset)
|
||||||
|
, layer_height(layer_height)
|
||||||
, print_thin_walls(Slic3r::Arachne::fill_outline_gaps)
|
, print_thin_walls(Slic3r::Arachne::fill_outline_gaps)
|
||||||
, min_feature_size(scaled<coord_t>(print_object_config.min_feature_size.value))
|
, min_feature_size(scaled<coord_t>(print_object_config.min_feature_size.value))
|
||||||
, min_bead_width(scaled<coord_t>(print_object_config.min_bead_width.value))
|
, min_bead_width(scaled<coord_t>(print_object_config.min_bead_width.value))
|
||||||
, small_area_length(static_cast<double>(bead_width_0) / 2.)
|
, small_area_length(static_cast<double>(bead_width_0) / 2.)
|
||||||
|
, wall_transition_filter_deviation(scaled<coord_t>(print_object_config.wall_transition_filter_deviation.value))
|
||||||
|
, wall_transition_length(scaled<coord_t>(print_object_config.wall_transition_length.value))
|
||||||
, toolpaths_generated(false)
|
, toolpaths_generated(false)
|
||||||
, print_object_config(print_object_config)
|
, print_object_config(print_object_config)
|
||||||
{
|
{
|
||||||
if (const auto &min_bead_width_opt = print_object_config.min_bead_width; min_bead_width_opt.percent) {
|
assert(!print_config.nozzle_diameter.empty());
|
||||||
assert(!print_config.nozzle_diameter.empty());
|
this->min_nozzle_diameter = float(*std::min_element(print_config.nozzle_diameter.values.begin(), print_config.nozzle_diameter.values.end()));
|
||||||
double min_nozzle_diameter = *std::min_element(print_config.nozzle_diameter.values.begin(), print_config.nozzle_diameter.values.end());
|
|
||||||
this->min_bead_width = scaled<coord_t>(min_bead_width_opt.value * 0.01 * min_nozzle_diameter);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (const auto &wall_transition_filter_deviation_opt = print_object_config.wall_transition_filter_deviation; wall_transition_filter_deviation_opt.percent) {
|
if (const auto &min_feature_size_opt = print_object_config.min_feature_size; min_feature_size_opt.percent)
|
||||||
assert(!print_config.nozzle_diameter.empty());
|
this->min_feature_size = scaled<coord_t>(min_feature_size_opt.value * 0.01 * this->min_nozzle_diameter);
|
||||||
double min_nozzle_diameter = *std::min_element(print_config.nozzle_diameter.values.begin(), print_config.nozzle_diameter.values.end());
|
|
||||||
this->wall_transition_filter_deviation = scaled<coord_t>(wall_transition_filter_deviation_opt.value * 0.01 * min_nozzle_diameter);
|
if (const auto &min_bead_width_opt = print_object_config.min_bead_width; min_bead_width_opt.percent)
|
||||||
}
|
this->min_bead_width = scaled<coord_t>(min_bead_width_opt.value * 0.01 * this->min_nozzle_diameter);
|
||||||
|
|
||||||
|
if (const auto &wall_transition_filter_deviation_opt = print_object_config.wall_transition_filter_deviation; wall_transition_filter_deviation_opt.percent)
|
||||||
|
this->wall_transition_filter_deviation = scaled<coord_t>(wall_transition_filter_deviation_opt.value * 0.01 * this->min_nozzle_diameter);
|
||||||
|
|
||||||
|
if (const auto &wall_transition_length_opt = print_object_config.wall_transition_length; wall_transition_length_opt.percent)
|
||||||
|
this->wall_transition_length = scaled<coord_t>(wall_transition_length_opt.value * 0.01 * this->min_nozzle_diameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void simplify(Polygon &thiss, const int64_t smallest_line_segment_squared, const int64_t allowed_error_distance_squared)
|
void simplify(Polygon &thiss, const int64_t smallest_line_segment_squared, const int64_t allowed_error_distance_squared)
|
||||||
@ -322,60 +329,46 @@ void removeSmallAreas(Polygons &thiss, const double min_area_size, const bool re
|
|||||||
};
|
};
|
||||||
|
|
||||||
auto new_end = thiss.end();
|
auto new_end = thiss.end();
|
||||||
if(remove_holes)
|
if (remove_holes) {
|
||||||
{
|
for (auto it = thiss.begin(); it < new_end;) {
|
||||||
for(auto it = thiss.begin(); it < new_end; it++)
|
// All polygons smaller than target are removed by replacing them with a polygon from the back of the vector.
|
||||||
{
|
if (fabs(ClipperLib::Area(to_path(*it))) < min_area_size) {
|
||||||
// All polygons smaller than target are removed by replacing them with a polygon from the back of the vector
|
--new_end;
|
||||||
if(fabs(ClipperLib::Area(to_path(*it))) < min_area_size)
|
|
||||||
{
|
|
||||||
new_end--;
|
|
||||||
*it = std::move(*new_end);
|
*it = std::move(*new_end);
|
||||||
it--; // wind back the iterator such that the polygon just swaped in is checked next
|
continue; // Don't increment the iterator such that the polygon just swapped in is checked next.
|
||||||
}
|
}
|
||||||
|
++it;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// For each polygon, computes the signed area, move small outlines at the end of the vector and keep pointer on small holes
|
// For each polygon, computes the signed area, move small outlines at the end of the vector and keep pointer on small holes
|
||||||
std::vector<Polygon> small_holes;
|
std::vector<Polygon> small_holes;
|
||||||
for(auto it = thiss.begin(); it < new_end; it++) {
|
for (auto it = thiss.begin(); it < new_end;) {
|
||||||
double area = ClipperLib::Area(to_path(*it));
|
if (double area = ClipperLib::Area(to_path(*it)); fabs(area) < min_area_size) {
|
||||||
if (fabs(area) < min_area_size)
|
if (area >= 0) {
|
||||||
{
|
--new_end;
|
||||||
if(area >= 0)
|
if (it < new_end) {
|
||||||
{
|
|
||||||
new_end--;
|
|
||||||
if(it < new_end) {
|
|
||||||
std::swap(*new_end, *it);
|
std::swap(*new_end, *it);
|
||||||
it--;
|
continue;
|
||||||
}
|
} else { // Don't self-swap the last Path
|
||||||
else
|
|
||||||
{ // Don't self-swap the last Path
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
small_holes.push_back(*it);
|
small_holes.push_back(*it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
++it;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removes small holes that have their first point inside one of the removed outlines
|
// Removes small holes that have their first point inside one of the removed outlines
|
||||||
// Iterating in reverse ensures that unprocessed small holes won't be moved
|
// Iterating in reverse ensures that unprocessed small holes won't be moved
|
||||||
const auto removed_outlines_start = new_end;
|
const auto removed_outlines_start = new_end;
|
||||||
for(auto hole_it = small_holes.rbegin(); hole_it < small_holes.rend(); hole_it++)
|
for (auto hole_it = small_holes.rbegin(); hole_it < small_holes.rend(); hole_it++)
|
||||||
{
|
for (auto outline_it = removed_outlines_start; outline_it < thiss.end(); outline_it++)
|
||||||
for(auto outline_it = removed_outlines_start; outline_it < thiss.end() ; outline_it++)
|
if (Polygon(*outline_it).contains(*hole_it->begin())) {
|
||||||
{
|
|
||||||
if(Polygon(*outline_it).contains(*hole_it->begin())) {
|
|
||||||
new_end--;
|
new_end--;
|
||||||
*hole_it = std::move(*new_end);
|
*hole_it = std::move(*new_end);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
thiss.resize(new_end-thiss.begin());
|
thiss.resize(new_end-thiss.begin());
|
||||||
}
|
}
|
||||||
@ -481,7 +474,7 @@ const std::vector<VariableWidthLines> &WallToolPaths::generate()
|
|||||||
// The functions above could produce intersecting polygons that could cause a crash inside Arachne.
|
// The functions above could produce intersecting polygons that could cause a crash inside Arachne.
|
||||||
// Applying Clipper union should be enough to get rid of this issue.
|
// Applying Clipper union should be enough to get rid of this issue.
|
||||||
// Clipper union also fixed an issue in Arachne that in post-processing Voronoi diagram, some edges
|
// Clipper union also fixed an issue in Arachne that in post-processing Voronoi diagram, some edges
|
||||||
// didn't have twin edges (this probably isn't an issue in Boost Voronoi generator).
|
// didn't have twin edges. (a non-planar Voronoi diagram probably caused this).
|
||||||
prepared_outline = union_(prepared_outline);
|
prepared_outline = union_(prepared_outline);
|
||||||
|
|
||||||
if (area(prepared_outline) <= 0) {
|
if (area(prepared_outline) <= 0) {
|
||||||
@ -489,9 +482,12 @@ const std::vector<VariableWidthLines> &WallToolPaths::generate()
|
|||||||
return toolpaths;
|
return toolpaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
const coord_t wall_transition_length = scaled<coord_t>(this->print_object_config.wall_transition_length.value);
|
const float external_perimeter_extrusion_width = Flow::rounded_rectangle_extrusion_width_from_spacing(unscale<float>(bead_width_0), float(this->layer_height));
|
||||||
const double wall_split_middle_threshold = this->print_object_config.wall_split_middle_threshold.value / 100.; // For an uneven nr. of lines: When to split the middle wall into two.
|
const float perimeter_extrusion_width = Flow::rounded_rectangle_extrusion_width_from_spacing(unscale<float>(bead_width_x), float(this->layer_height));
|
||||||
const double wall_add_middle_threshold = this->print_object_config.wall_add_middle_threshold.value / 100.; // For an even nr. of lines: When to add a new middle in between the innermost two walls.
|
|
||||||
|
const double wall_split_middle_threshold = std::clamp(2. * unscaled<double>(this->min_bead_width) / external_perimeter_extrusion_width - 1., 0.01, 0.99); // For an uneven nr. of lines: When to split the middle wall into two.
|
||||||
|
const double wall_add_middle_threshold = std::clamp(unscaled<double>(this->min_bead_width) / perimeter_extrusion_width, 0.01, 0.99); // For an even nr. of lines: When to add a new middle in between the innermost two walls.
|
||||||
|
|
||||||
const int wall_distribution_count = this->print_object_config.wall_distribution_count.value;
|
const int wall_distribution_count = this->print_object_config.wall_distribution_count.value;
|
||||||
const size_t max_bead_count = (inset_count < std::numeric_limits<coord_t>::max() / 2) ? 2 * inset_count : std::numeric_limits<coord_t>::max();
|
const size_t max_bead_count = (inset_count < std::numeric_limits<coord_t>::max() / 2) ? 2 * inset_count : std::numeric_limits<coord_t>::max();
|
||||||
const auto beading_strat = BeadingStrategyFactory::makeStrategy
|
const auto beading_strat = BeadingStrategyFactory::makeStrategy
|
||||||
@ -619,6 +615,14 @@ void WallToolPaths::stitchToolPaths(std::vector<VariableWidthLines> &toolpaths,
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PolylineStitcher, in some cases, produced closed extrusion (polygons),
|
||||||
|
// but the endpoints differ by a small distance. So we reconnect them.
|
||||||
|
// FIXME Lukas H.: Investigate more deeply why it is happening.
|
||||||
|
if (wall_polygon.junctions.front().p != wall_polygon.junctions.back().p &&
|
||||||
|
(wall_polygon.junctions.back().p - wall_polygon.junctions.front().p).cast<double>().norm() < stitch_distance) {
|
||||||
|
wall_polygon.junctions.emplace_back(wall_polygon.junctions.front());
|
||||||
|
}
|
||||||
wall_polygon.is_closed = true;
|
wall_polygon.is_closed = true;
|
||||||
wall_lines.emplace_back(std::move(wall_polygon)); // add stitched polygons to result
|
wall_lines.emplace_back(std::move(wall_polygon)); // add stitched polygons to result
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ public:
|
|||||||
* \param inset_count The maximum number of parallel extrusion lines that make up the wall
|
* \param inset_count The maximum number of parallel extrusion lines that make up the wall
|
||||||
* \param wall_0_inset How far to inset the outer wall, to make it adhere better to other walls.
|
* \param wall_0_inset How far to inset the outer wall, to make it adhere better to other walls.
|
||||||
*/
|
*/
|
||||||
WallToolPaths(const Polygons& outline, coord_t bead_width_0, coord_t bead_width_x, size_t inset_count, coord_t wall_0_inset, const PrintObjectConfig &print_object_config, const PrintConfig &print_config);
|
WallToolPaths(const Polygons& outline, coord_t bead_width_0, coord_t bead_width_x, size_t inset_count, coord_t wall_0_inset, coordf_t layer_height, const PrintObjectConfig &print_object_config, const PrintConfig &print_config);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Generates the Toolpaths
|
* Generates the Toolpaths
|
||||||
@ -110,14 +110,17 @@ private:
|
|||||||
coord_t bead_width_x; //<! The subsequently extrusion line width with which libArachne generates its walls if WallToolPaths was called with the nominal_bead_width Constructor this is the same as bead_width_0
|
coord_t bead_width_x; //<! The subsequently extrusion line width with which libArachne generates its walls if WallToolPaths was called with the nominal_bead_width Constructor this is the same as bead_width_0
|
||||||
size_t inset_count; //<! The maximum number of walls to generate
|
size_t inset_count; //<! The maximum number of walls to generate
|
||||||
coord_t wall_0_inset; //<! How far to inset the outer wall. Should only be applied when printing the actual walls, not extra infill/skin/support walls.
|
coord_t wall_0_inset; //<! How far to inset the outer wall. Should only be applied when printing the actual walls, not extra infill/skin/support walls.
|
||||||
|
coordf_t layer_height;
|
||||||
bool print_thin_walls; //<! Whether to enable the widening beading meta-strategy for thin features
|
bool print_thin_walls; //<! Whether to enable the widening beading meta-strategy for thin features
|
||||||
coord_t min_feature_size; //<! The minimum size of the features that can be widened by the widening beading meta-strategy. Features thinner than that will not be printed
|
coord_t min_feature_size; //<! The minimum size of the features that can be widened by the widening beading meta-strategy. Features thinner than that will not be printed
|
||||||
coord_t min_bead_width; //<! The minimum bead size to use when widening thin model features with the widening beading meta-strategy
|
coord_t min_bead_width; //<! The minimum bead size to use when widening thin model features with the widening beading meta-strategy
|
||||||
double small_area_length; //<! The length of the small features which are to be filtered out, this is squared into a surface
|
double small_area_length; //<! The length of the small features which are to be filtered out, this is squared into a surface
|
||||||
|
coord_t wall_transition_filter_deviation; //!< The allowed line width deviation induced by filtering
|
||||||
|
coord_t wall_transition_length;
|
||||||
|
float min_nozzle_diameter;
|
||||||
bool toolpaths_generated; //<! Are the toolpaths generated
|
bool toolpaths_generated; //<! Are the toolpaths generated
|
||||||
std::vector<VariableWidthLines> toolpaths; //<! The generated toolpaths
|
std::vector<VariableWidthLines> toolpaths; //<! The generated toolpaths
|
||||||
Polygons inner_contour; //<! The inner contour of the generated toolpaths
|
Polygons inner_contour; //<! The inner contour of the generated toolpaths
|
||||||
coord_t wall_transition_filter_deviation; //!< The allowed line width deviation induced by filtering
|
|
||||||
const PrintObjectConfig &print_object_config;
|
const PrintObjectConfig &print_object_config;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -268,13 +268,13 @@ void extrusion_paths_append(ExtrusionPaths &dst, const ClipperLib_Z::Paths &extr
|
|||||||
{
|
{
|
||||||
for (const ClipperLib_Z::Path &extrusion_path : extrusion_paths) {
|
for (const ClipperLib_Z::Path &extrusion_path : extrusion_paths) {
|
||||||
ThickPolyline thick_polyline = Arachne::to_thick_polyline(extrusion_path);
|
ThickPolyline thick_polyline = Arachne::to_thick_polyline(extrusion_path);
|
||||||
Slic3r::append(dst, thick_polyline_to_extrusion_paths(thick_polyline, role, flow, scaled<float>(0.05), SCALED_EPSILON));
|
Slic3r::append(dst, thick_polyline_to_extrusion_paths(thick_polyline, role, flow, scaled<float>(0.05), float(SCALED_EPSILON)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void extrusion_paths_append(ExtrusionPaths &dst, const Arachne::ExtrusionLine &extrusion, const ExtrusionRole role, const Flow &flow)
|
void extrusion_paths_append(ExtrusionPaths &dst, const Arachne::ExtrusionLine &extrusion, const ExtrusionRole role, const Flow &flow)
|
||||||
{
|
{
|
||||||
ThickPolyline thick_polyline = Arachne::to_thick_polyline(extrusion);
|
ThickPolyline thick_polyline = Arachne::to_thick_polyline(extrusion);
|
||||||
Slic3r::append(dst, thick_polyline_to_extrusion_paths(thick_polyline, role, flow, scaled<float>(0.05), SCALED_EPSILON));
|
Slic3r::append(dst, thick_polyline_to_extrusion_paths(thick_polyline, role, flow, scaled<float>(0.05), float(SCALED_EPSILON)));
|
||||||
}
|
}
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
@ -109,6 +109,8 @@ set(SLIC3R_SOURCES
|
|||||||
Format/SL1_SVG.cpp
|
Format/SL1_SVG.cpp
|
||||||
Format/pwmx.hpp
|
Format/pwmx.hpp
|
||||||
Format/pwmx.cpp
|
Format/pwmx.cpp
|
||||||
|
Format/STEP.hpp
|
||||||
|
Format/STEP.cpp
|
||||||
GCode/ThumbnailData.cpp
|
GCode/ThumbnailData.cpp
|
||||||
GCode/ThumbnailData.hpp
|
GCode/ThumbnailData.hpp
|
||||||
GCode/Thumbnails.cpp
|
GCode/Thumbnails.cpp
|
||||||
@ -387,7 +389,7 @@ find_package(CGAL REQUIRED)
|
|||||||
cmake_policy(POP)
|
cmake_policy(POP)
|
||||||
|
|
||||||
add_library(libslic3r_cgal STATIC MeshBoolean.cpp MeshBoolean.hpp TryCatchSignal.hpp
|
add_library(libslic3r_cgal STATIC MeshBoolean.cpp MeshBoolean.hpp TryCatchSignal.hpp
|
||||||
TryCatchSignal.cpp)
|
TryCatchSignal.cpp Geometry/VoronoiUtilsCgal.hpp Geometry/VoronoiUtilsCgal.cpp)
|
||||||
target_include_directories(libslic3r_cgal PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
target_include_directories(libslic3r_cgal PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
# Reset compile options of libslic3r_cgal. Despite it being linked privately, CGAL options
|
# Reset compile options of libslic3r_cgal. Despite it being linked privately, CGAL options
|
||||||
@ -442,6 +444,11 @@ target_link_libraries(libslic3r
|
|||||||
qoi
|
qoi
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (APPLE)
|
||||||
|
# TODO: we need to fix notarization with the separate shared library
|
||||||
|
target_link_libraries(libslic3r OCCTWrapper)
|
||||||
|
endif ()
|
||||||
|
|
||||||
if (TARGET OpenVDB::openvdb)
|
if (TARGET OpenVDB::openvdb)
|
||||||
target_link_libraries(libslic3r OpenVDB::openvdb)
|
target_link_libraries(libslic3r OpenVDB::openvdb)
|
||||||
endif()
|
endif()
|
||||||
|
@ -705,6 +705,8 @@ Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygo
|
|||||||
{ return _clipper_pl_open(ClipperLib::ctDifference, ClipperUtils::PolylinesProvider(subject), ClipperUtils::PolygonsProvider(clip)); }
|
{ return _clipper_pl_open(ClipperLib::ctDifference, ClipperUtils::PolylinesProvider(subject), ClipperUtils::PolygonsProvider(clip)); }
|
||||||
Slic3r::Polylines diff_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip)
|
Slic3r::Polylines diff_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip)
|
||||||
{ return _clipper_pl_open(ClipperLib::ctDifference, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::ExPolygonProvider(clip)); }
|
{ return _clipper_pl_open(ClipperLib::ctDifference, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::ExPolygonProvider(clip)); }
|
||||||
|
Slic3r::Polylines diff_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygons &clip)
|
||||||
|
{ return _clipper_pl_open(ClipperLib::ctDifference, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::ExPolygonsProvider(clip)); }
|
||||||
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip)
|
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip)
|
||||||
{ return _clipper_pl_open(ClipperLib::ctDifference, ClipperUtils::PolylinesProvider(subject), ClipperUtils::ExPolygonProvider(clip)); }
|
{ return _clipper_pl_open(ClipperLib::ctDifference, ClipperUtils::PolylinesProvider(subject), ClipperUtils::ExPolygonProvider(clip)); }
|
||||||
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &clip)
|
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &clip)
|
||||||
|
@ -408,6 +408,7 @@ Slic3r::ExPolygons diff_ex(const Slic3r::Surfaces &subject, const Slic3r::Surfac
|
|||||||
Slic3r::ExPolygons diff_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
Slic3r::ExPolygons diff_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||||
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip);
|
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip);
|
||||||
Slic3r::Polylines diff_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip);
|
Slic3r::Polylines diff_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip);
|
||||||
|
Slic3r::Polylines diff_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygons &clip);
|
||||||
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip);
|
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip);
|
||||||
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &clip);
|
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &clip);
|
||||||
Slic3r::Polylines diff_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip);
|
Slic3r::Polylines diff_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip);
|
||||||
|
@ -389,6 +389,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
|||||||
params.anchor_length_max = surface_fill.params.anchor_length_max;
|
params.anchor_length_max = surface_fill.params.anchor_length_max;
|
||||||
params.resolution = resolution;
|
params.resolution = resolution;
|
||||||
params.use_arachne = perimeter_generator == PerimeterGeneratorType::Arachne && surface_fill.params.pattern == ipConcentric;
|
params.use_arachne = perimeter_generator == PerimeterGeneratorType::Arachne && surface_fill.params.pattern == ipConcentric;
|
||||||
|
params.layer_height = m_regions[surface_fill.region_id]->layer()->height;
|
||||||
|
|
||||||
for (ExPolygon &expoly : surface_fill.expolygons) {
|
for (ExPolygon &expoly : surface_fill.expolygons) {
|
||||||
// Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon.
|
// Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon.
|
||||||
|
@ -61,6 +61,8 @@ struct FillParams
|
|||||||
|
|
||||||
// For Concentric infill, to switch between Classic and Arachne.
|
// For Concentric infill, to switch between Classic and Arachne.
|
||||||
bool use_arachne { false };
|
bool use_arachne { false };
|
||||||
|
// Layer height for Concentric infill with Arachne.
|
||||||
|
coordf_t layer_height { 0.f };
|
||||||
};
|
};
|
||||||
static_assert(IsTriviallyCopyable<FillParams>::value, "FillParams class is not POD (and it should be - see constructor).");
|
static_assert(IsTriviallyCopyable<FillParams>::value, "FillParams class is not POD (and it should be - see constructor).");
|
||||||
|
|
||||||
|
@ -77,8 +77,8 @@ void FillConcentric::_fill_surface_single(const FillParams ¶ms,
|
|||||||
|
|
||||||
if (params.density > 0.9999f && !params.dont_adjust) {
|
if (params.density > 0.9999f && !params.dont_adjust) {
|
||||||
coord_t loops_count = std::max(bbox_size.x(), bbox_size.y()) / min_spacing + 1;
|
coord_t loops_count = std::max(bbox_size.x(), bbox_size.y()) / min_spacing + 1;
|
||||||
Polygons polygons = offset(expolygon, min_spacing / 2);
|
Polygons polygons = offset(expolygon, float(min_spacing) / 2.f);
|
||||||
Arachne::WallToolPaths wallToolPaths(polygons, min_spacing, min_spacing, loops_count, 0, *this->print_object_config, *this->print_config);
|
Arachne::WallToolPaths wallToolPaths(polygons, min_spacing, min_spacing, loops_count, 0, params.layer_height, *this->print_object_config, *this->print_config);
|
||||||
|
|
||||||
std::vector<Arachne::VariableWidthLines> loops = wallToolPaths.getToolPaths();
|
std::vector<Arachne::VariableWidthLines> loops = wallToolPaths.getToolPaths();
|
||||||
std::vector<const Arachne::ExtrusionLine *> all_extrusions;
|
std::vector<const Arachne::ExtrusionLine *> all_extrusions;
|
||||||
|
@ -70,7 +70,7 @@ void Generator::generateInitialInternalOverhangs(const PrintObject &print_object
|
|||||||
// Remove the part of the infill area that is already supported by the walls.
|
// Remove the part of the infill area that is already supported by the walls.
|
||||||
Polygons overhang = diff(offset(infill_area_here, -float(m_wall_supporting_radius)), infill_area_above);
|
Polygons overhang = diff(offset(infill_area_here, -float(m_wall_supporting_radius)), infill_area_above);
|
||||||
// Filter out unprintable polygons and near degenerated polygons (three almost collinear points and so).
|
// Filter out unprintable polygons and near degenerated polygons (three almost collinear points and so).
|
||||||
overhang = opening(overhang, SCALED_EPSILON, SCALED_EPSILON);
|
overhang = opening(overhang, float(SCALED_EPSILON), float(SCALED_EPSILON));
|
||||||
|
|
||||||
m_overhang_per_layer[layer_nr] = overhang;
|
m_overhang_per_layer[layer_nr] = overhang;
|
||||||
infill_area_above = std::move(infill_area_here);
|
infill_area_above = std::move(infill_area_here);
|
||||||
|
@ -457,6 +457,7 @@ namespace Slic3r {
|
|||||||
|
|
||||||
bool load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, bool check_version);
|
bool load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, bool check_version);
|
||||||
unsigned int version() const { return m_version; }
|
unsigned int version() const { return m_version; }
|
||||||
|
boost::optional<Semver> prusaslicer_generator_version() const { return m_prusaslicer_generator_version; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _destroy_xml_parser();
|
void _destroy_xml_parser();
|
||||||
@ -3147,8 +3148,7 @@ bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archiv
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Perform conversions based on the config values available.
|
// Perform conversions based on the config values available.
|
||||||
//FIXME provide a version of PrusaSlicer that stored the project file (3MF).
|
static void handle_legacy_project_loaded(unsigned int version_project_file, DynamicPrintConfig& config, const boost::optional<Semver>& prusaslicer_generator_version)
|
||||||
static void handle_legacy_project_loaded(unsigned int version_project_file, DynamicPrintConfig& config)
|
|
||||||
{
|
{
|
||||||
if (! config.has("brim_separation")) {
|
if (! config.has("brim_separation")) {
|
||||||
if (auto *opt_elephant_foot = config.option<ConfigOptionFloat>("elefant_foot_compensation", false); opt_elephant_foot) {
|
if (auto *opt_elephant_foot = config.option<ConfigOptionFloat>("elefant_foot_compensation", false); opt_elephant_foot) {
|
||||||
@ -3157,6 +3157,23 @@ static void handle_legacy_project_loaded(unsigned int version_project_file, Dyna
|
|||||||
opt_brim_separation->value = opt_elephant_foot->value;
|
opt_brim_separation->value = opt_elephant_foot->value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In PrusaSlicer 2.5.0-alpha2 and 2.5.0-alpha3, we introduce several parameters for Arachne that depend
|
||||||
|
// on nozzle size . Later we decided to make default values for those parameters computed automatically
|
||||||
|
// until the user changes them.
|
||||||
|
if (prusaslicer_generator_version && *prusaslicer_generator_version >= *Semver::parse("2.5.0-alpha2") && *prusaslicer_generator_version <= *Semver::parse("2.5.0-alpha3")) {
|
||||||
|
if (auto *opt_wall_transition_length = config.option<ConfigOptionFloatOrPercent>("wall_transition_length", false);
|
||||||
|
opt_wall_transition_length && !opt_wall_transition_length->percent && opt_wall_transition_length->value == 0.4) {
|
||||||
|
opt_wall_transition_length->percent = true;
|
||||||
|
opt_wall_transition_length->value = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto *opt_min_feature_size = config.option<ConfigOptionFloatOrPercent>("min_feature_size", false);
|
||||||
|
opt_min_feature_size && !opt_min_feature_size->percent && opt_min_feature_size->value == 0.1) {
|
||||||
|
opt_min_feature_size->percent = true;
|
||||||
|
opt_min_feature_size->value = 25;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_project_3mf(const std::string& filename)
|
bool is_project_3mf(const std::string& filename)
|
||||||
@ -3199,7 +3216,7 @@ bool load_3mf(const char* path, DynamicPrintConfig& config, ConfigSubstitutionCo
|
|||||||
_3MF_Importer importer;
|
_3MF_Importer importer;
|
||||||
importer.load_model_from_file(path, *model, config, config_substitutions, check_version);
|
importer.load_model_from_file(path, *model, config, config_substitutions, check_version);
|
||||||
importer.log_errors();
|
importer.log_errors();
|
||||||
handle_legacy_project_loaded(importer.version(), config);
|
handle_legacy_project_loaded(importer.version(), config, importer.prusaslicer_generator_version());
|
||||||
|
|
||||||
return !model->objects.empty() || !config.empty();
|
return !model->objects.empty() || !config.empty();
|
||||||
}
|
}
|
||||||
|
131
src/libslic3r/Format/STEP.cpp
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
#include "STEP.hpp"
|
||||||
|
#include "occt_wrapper/OCCTWrapper.hpp"
|
||||||
|
|
||||||
|
#include "libslic3r/Model.hpp"
|
||||||
|
#include "libslic3r/TriangleMesh.hpp"
|
||||||
|
#include "libslic3r/Utils.hpp"
|
||||||
|
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
#include <boost/dll/runtime_symbol_info.hpp>
|
||||||
|
#include <boost/log/trivial.hpp>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include<windows.h>
|
||||||
|
#else
|
||||||
|
#include<occt_wrapper/OCCTWrapper.hpp>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
#if __APPLE__
|
||||||
|
extern "C" bool load_step_internal(const char *path, OCCTResult* res);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
LoadStepFn get_load_step_fn()
|
||||||
|
{
|
||||||
|
static LoadStepFn load_step_fn = nullptr;
|
||||||
|
|
||||||
|
#ifndef __APPLE__
|
||||||
|
constexpr const char* fn_name = "load_step_internal";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!load_step_fn) {
|
||||||
|
auto libpath = boost::dll::program_location().parent_path();
|
||||||
|
#ifdef _WIN32
|
||||||
|
libpath /= "OCCTWrapper.dll";
|
||||||
|
HMODULE module = LoadLibraryW(libpath.wstring().c_str());
|
||||||
|
if (module == NULL)
|
||||||
|
throw Slic3r::RuntimeError("Cannot load OCCTWrapper.dll");
|
||||||
|
|
||||||
|
try {
|
||||||
|
FARPROC farproc = GetProcAddress(module, fn_name);
|
||||||
|
if (! farproc) {
|
||||||
|
DWORD ec = GetLastError();
|
||||||
|
throw Slic3r::RuntimeError(std::string("Cannot load function from OCCTWrapper.dll: ") + fn_name
|
||||||
|
+ "\n\nError code: " + std::to_string(ec));
|
||||||
|
}
|
||||||
|
load_step_fn = reinterpret_cast<LoadStepFn>(farproc);
|
||||||
|
} catch (const Slic3r::RuntimeError&) {
|
||||||
|
FreeLibrary(module);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
#elif __APPLE__
|
||||||
|
load_step_fn = &load_step_internal;
|
||||||
|
#else
|
||||||
|
libpath /= "OCCTWrapper.so";
|
||||||
|
void *plugin_ptr = dlopen(libpath.c_str(), RTLD_NOW | RTLD_GLOBAL);
|
||||||
|
|
||||||
|
if (plugin_ptr) {
|
||||||
|
load_step_fn = reinterpret_cast<LoadStepFn>(dlsym(plugin_ptr, fn_name));
|
||||||
|
if (!load_step_fn) {
|
||||||
|
dlclose(plugin_ptr);
|
||||||
|
throw Slic3r::RuntimeError(std::string("Cannot load function from OCCTWrapper.dll: ") + fn_name
|
||||||
|
+ "\n\n" + dlerror());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw Slic3r::RuntimeError(std::string("Cannot load OCCTWrapper.dll:\n\n") + dlerror());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return load_step_fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool load_step(const char *path, Model *model /*BBS:, ImportStepProgressFn proFn*/)
|
||||||
|
{
|
||||||
|
OCCTResult occt_object;
|
||||||
|
|
||||||
|
LoadStepFn load_step_fn = get_load_step_fn();
|
||||||
|
|
||||||
|
if (!load_step_fn)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
load_step_fn(path, &occt_object);
|
||||||
|
|
||||||
|
assert(! occt_object.volumes.empty());
|
||||||
|
|
||||||
|
assert(boost::algorithm::iends_with(occt_object.object_name, ".stp")
|
||||||
|
|| boost::algorithm::iends_with(occt_object.object_name, ".step"));
|
||||||
|
occt_object.object_name.erase(occt_object.object_name.find("."));
|
||||||
|
assert(! occt_object.object_name.empty());
|
||||||
|
|
||||||
|
|
||||||
|
ModelObject* new_object = model->add_object();
|
||||||
|
new_object->input_file = path;
|
||||||
|
if (new_object->volumes.size() == 1 && ! occt_object.volumes.front().volume_name.empty())
|
||||||
|
new_object->name = new_object->volumes.front()->name;
|
||||||
|
else
|
||||||
|
new_object->name = occt_object.object_name;
|
||||||
|
|
||||||
|
|
||||||
|
for (size_t i=0; i<occt_object.volumes.size(); ++i) {
|
||||||
|
indexed_triangle_set its;
|
||||||
|
for (size_t j=0; j<occt_object.volumes[i].vertices.size(); ++j)
|
||||||
|
its.vertices.emplace_back(Vec3f(occt_object.volumes[i].vertices[j][0],
|
||||||
|
occt_object.volumes[i].vertices[j][1],
|
||||||
|
occt_object.volumes[i].vertices[j][2]));
|
||||||
|
for (size_t j=0; j<occt_object.volumes[i].indices.size(); ++j)
|
||||||
|
its.indices.emplace_back(Vec3i(occt_object.volumes[i].indices[j][0],
|
||||||
|
occt_object.volumes[i].indices[j][1],
|
||||||
|
occt_object.volumes[i].indices[j][2]));
|
||||||
|
its_merge_vertices(its, true);
|
||||||
|
TriangleMesh triangle_mesh(std::move(its));
|
||||||
|
ModelVolume* new_volume = new_object->add_volume(std::move(triangle_mesh));
|
||||||
|
|
||||||
|
new_volume->name = occt_object.volumes[i].volume_name.empty()
|
||||||
|
? std::string("Part") + std::to_string(i+1)
|
||||||
|
: occt_object.volumes[i].volume_name;
|
||||||
|
new_volume->source.input_file = path;
|
||||||
|
new_volume->source.object_idx = (int)model->objects.size() - 1;
|
||||||
|
new_volume->source.volume_idx = (int)new_object->volumes.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // namespace Slic3r
|
19
src/libslic3r/Format/STEP.hpp
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// Original implementation of STEP format import created by Bambulab.
|
||||||
|
// https://github.com/bambulab/BambuStudio
|
||||||
|
// Forked off commit 1555904, modified by Prusa Research.
|
||||||
|
|
||||||
|
#ifndef slic3r_Format_STEP_hpp_
|
||||||
|
#define slic3r_Format_STEP_hpp_
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
class Model;
|
||||||
|
|
||||||
|
//typedef std::function<void(int load_stage, int current, int total, bool& cancel)> ImportStepProgressFn;
|
||||||
|
|
||||||
|
// Load a step file into a provided model.
|
||||||
|
extern bool load_step(const char *path_str, Model *model /*LMBBS:, ImportStepProgressFn proFn = nullptr*/);
|
||||||
|
|
||||||
|
}; // namespace Slic3r
|
||||||
|
|
||||||
|
#endif /* slic3r_Format_STEP_hpp_ */
|
@ -3137,7 +3137,7 @@ bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role)
|
|||||||
if (role == erSupportMaterial) {
|
if (role == erSupportMaterial) {
|
||||||
const SupportLayer* support_layer = dynamic_cast<const SupportLayer*>(m_layer);
|
const SupportLayer* support_layer = dynamic_cast<const SupportLayer*>(m_layer);
|
||||||
//FIXME support_layer->support_islands.contains should use some search structure!
|
//FIXME support_layer->support_islands.contains should use some search structure!
|
||||||
if (support_layer != NULL && ! intersection_pl(travel, support_layer->support_islands).empty())
|
if (support_layer != NULL && diff_pl(travel, support_layer->support_islands).empty())
|
||||||
// skip retraction if this is a travel move inside a support material island
|
// skip retraction if this is a travel move inside a support material island
|
||||||
//FIXME not retracting over a long path may cause oozing, which in turn may result in missing material
|
//FIXME not retracting over a long path may cause oozing, which in turn may result in missing material
|
||||||
// at the end of the extrusion path!
|
// at the end of the extrusion path!
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
#include <string.h>
|
#include <cstring>
|
||||||
#include <float.h>
|
#include <cfloat>
|
||||||
|
|
||||||
#include "../libslic3r.h"
|
#include "../libslic3r.h"
|
||||||
#include "../PrintConfig.hpp"
|
#include "../PrintConfig.hpp"
|
||||||
@ -31,7 +31,8 @@ PressureEqualizer::PressureEqualizer(const Slic3r::GCodeConfig &config) : m_use_
|
|||||||
{
|
{
|
||||||
// Preallocate some data, so that output_buffer.data() will return an empty string.
|
// Preallocate some data, so that output_buffer.data() will return an empty string.
|
||||||
output_buffer.assign(32, 0);
|
output_buffer.assign(32, 0);
|
||||||
output_buffer_length = 0;
|
output_buffer_length = 0;
|
||||||
|
output_buffer_prev_length = 0;
|
||||||
|
|
||||||
m_current_extruder = 0;
|
m_current_extruder = 0;
|
||||||
// Zero the position of the XYZE axes + the current feed
|
// Zero the position of the XYZE axes + the current feed
|
||||||
@ -58,13 +59,14 @@ PressureEqualizer::PressureEqualizer(const Slic3r::GCodeConfig &config) : m_use_
|
|||||||
extrusion_rate_slope.positive = m_max_volumetric_extrusion_rate_slope_positive;
|
extrusion_rate_slope.positive = m_max_volumetric_extrusion_rate_slope_positive;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't regulate the pressure in infill, gap fill and ironing.
|
// Don't regulate the pressure before and after gap-fill and ironing.
|
||||||
// TODO: Do we want to regulate pressure in erWipeTower, erCustom and erMixed?
|
for (const ExtrusionRole er : {erGapFill, erIroning}) {
|
||||||
for (const ExtrusionRole er : {erBridgeInfill, erGapFill, erIroning}) {
|
|
||||||
m_max_volumetric_extrusion_rate_slopes[er].negative = 0;
|
m_max_volumetric_extrusion_rate_slopes[er].negative = 0;
|
||||||
m_max_volumetric_extrusion_rate_slopes[er].positive = 0;
|
m_max_volumetric_extrusion_rate_slopes[er].positive = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
opened_extrude_set_speed_block = false;
|
||||||
|
|
||||||
#ifdef PRESSURE_EQUALIZER_STATISTIC
|
#ifdef PRESSURE_EQUALIZER_STATISTIC
|
||||||
m_stat.reset();
|
m_stat.reset();
|
||||||
#endif
|
#endif
|
||||||
@ -93,6 +95,7 @@ void PressureEqualizer::process_layer(const std::string &gcode)
|
|||||||
if (*gcode_begin == '\n')
|
if (*gcode_begin == '\n')
|
||||||
++gcode_begin;
|
++gcode_begin;
|
||||||
}
|
}
|
||||||
|
assert(!this->opened_extrude_set_speed_block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,9 +117,10 @@ LayerResult PressureEqualizer::process_layer(LayerResult &&input)
|
|||||||
LayerResult *prev_layer_result = m_layer_results.front();
|
LayerResult *prev_layer_result = m_layer_results.front();
|
||||||
m_layer_results.pop();
|
m_layer_results.pop();
|
||||||
|
|
||||||
output_buffer_length = 0;
|
output_buffer_length = 0;
|
||||||
|
output_buffer_prev_length = 0;
|
||||||
for (size_t line_idx = 0; line_idx < next_layer_first_idx; ++line_idx)
|
for (size_t line_idx = 0; line_idx < next_layer_first_idx; ++line_idx)
|
||||||
output_gcode_line(m_gcode_lines[line_idx]);
|
output_gcode_line(line_idx);
|
||||||
m_gcode_lines.erase(m_gcode_lines.begin(), m_gcode_lines.begin() + int(next_layer_first_idx));
|
m_gcode_lines.erase(m_gcode_lines.begin(), m_gcode_lines.begin() + int(next_layer_first_idx));
|
||||||
|
|
||||||
if (output_buffer_length > 0)
|
if (output_buffer_length > 0)
|
||||||
@ -131,9 +135,9 @@ LayerResult PressureEqualizer::process_layer(LayerResult &&input)
|
|||||||
// Is a white space?
|
// Is a white space?
|
||||||
static inline bool is_ws(const char c) { return c == ' ' || c == '\t'; }
|
static inline bool is_ws(const char c) { return c == ' ' || c == '\t'; }
|
||||||
// Is it an end of line? Consider a comment to be an end of line as well.
|
// Is it an end of line? Consider a comment to be an end of line as well.
|
||||||
static inline bool is_eol(const char c) { return c == 0 || c == '\r' || c == '\n' || c == ';'; };
|
static inline bool is_eol(const char c) { return c == 0 || c == '\r' || c == '\n' || c == ';'; }
|
||||||
// Is it a white space or end of line?
|
// Is it a white space or end of line?
|
||||||
static inline bool is_ws_or_eol(const char c) { return is_ws(c) || is_eol(c); };
|
static inline bool is_ws_or_eol(const char c) { return is_ws(c) || is_eol(c); }
|
||||||
|
|
||||||
// Eat whitespaces.
|
// Eat whitespaces.
|
||||||
static void eatws(const char *&line)
|
static void eatws(const char *&line)
|
||||||
@ -152,7 +156,7 @@ static inline int parse_int(const char *&line)
|
|||||||
throw Slic3r::InvalidArgument("PressureEqualizer: Error parsing an int");
|
throw Slic3r::InvalidArgument("PressureEqualizer: Error parsing an int");
|
||||||
line = endptr;
|
line = endptr;
|
||||||
return int(result);
|
return int(result);
|
||||||
};
|
}
|
||||||
|
|
||||||
float string_to_float_decimal_point(const char *line, const size_t str_len, size_t* pos)
|
float string_to_float_decimal_point(const char *line, const size_t str_len, size_t* pos)
|
||||||
{
|
{
|
||||||
@ -173,7 +177,7 @@ static inline float parse_float(const char *&line, const size_t line_length)
|
|||||||
throw Slic3r::RuntimeError("PressureEqualizer: Error parsing a float");
|
throw Slic3r::RuntimeError("PressureEqualizer: Error parsing a float");
|
||||||
line = line + endptr;
|
line = line + endptr;
|
||||||
return result;
|
return result;
|
||||||
};
|
}
|
||||||
|
|
||||||
bool PressureEqualizer::process_line(const char *line, const char *line_end, GCodeLine &buf)
|
bool PressureEqualizer::process_line(const char *line, const char *line_end, GCodeLine &buf)
|
||||||
{
|
{
|
||||||
@ -209,6 +213,16 @@ bool PressureEqualizer::process_line(const char *line, const char *line_end, GCo
|
|||||||
buf.max_volumetric_extrusion_rate_slope_negative = 0.f;
|
buf.max_volumetric_extrusion_rate_slope_negative = 0.f;
|
||||||
buf.extrusion_role = m_current_extrusion_role;
|
buf.extrusion_role = m_current_extrusion_role;
|
||||||
|
|
||||||
|
std::string str_line(line, line_end);
|
||||||
|
const bool found_extrude_set_speed_tag = boost::contains(str_line, EXTRUDE_SET_SPEED_TAG);
|
||||||
|
const bool found_extrude_end_tag = boost::contains(str_line, EXTRUDE_END_TAG);
|
||||||
|
assert(!found_extrude_set_speed_tag || !found_extrude_end_tag);
|
||||||
|
|
||||||
|
if (found_extrude_set_speed_tag)
|
||||||
|
this->opened_extrude_set_speed_block = true;
|
||||||
|
else if (found_extrude_end_tag)
|
||||||
|
this->opened_extrude_set_speed_block = false;
|
||||||
|
|
||||||
// Parse the G-code line, store the result into the buf.
|
// Parse the G-code line, store the result into the buf.
|
||||||
switch (toupper(*line ++)) {
|
switch (toupper(*line ++)) {
|
||||||
case 'G': {
|
case 'G': {
|
||||||
@ -228,6 +242,9 @@ bool PressureEqualizer::process_line(const char *line, const char *line_end, GCo
|
|||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
// G0, G1: A FFF 3D printer does not make a difference between the two.
|
// G0, G1: A FFF 3D printer does not make a difference between the two.
|
||||||
|
buf.adjustable_flow = this->opened_extrude_set_speed_block;
|
||||||
|
buf.extrude_set_speed_tag = found_extrude_set_speed_tag;
|
||||||
|
buf.extrude_end_tag = found_extrude_end_tag;
|
||||||
float new_pos[5];
|
float new_pos[5];
|
||||||
memcpy(new_pos, m_current_pos, sizeof(float)*5);
|
memcpy(new_pos, m_current_pos, sizeof(float)*5);
|
||||||
bool changed[5] = { false, false, false, false, false };
|
bool changed[5] = { false, false, false, false, false };
|
||||||
@ -372,8 +389,9 @@ bool PressureEqualizer::process_line(const char *line, const char *line_end, GCo
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PressureEqualizer::output_gcode_line(GCodeLine &line)
|
void PressureEqualizer::output_gcode_line(const size_t line_idx)
|
||||||
{
|
{
|
||||||
|
GCodeLine &line = m_gcode_lines[line_idx];
|
||||||
if (!line.modified) {
|
if (!line.modified) {
|
||||||
push_to_output(line.raw.data(), line.raw_length, true);
|
push_to_output(line.raw.data(), line.raw_length, true);
|
||||||
return;
|
return;
|
||||||
@ -389,7 +407,7 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line)
|
|||||||
// Emit the line with lowered extrusion rates.
|
// Emit the line with lowered extrusion rates.
|
||||||
float l = line.dist_xyz();
|
float l = line.dist_xyz();
|
||||||
if (auto nSegments = size_t(ceil(l / max_segment_length)); nSegments == 1) { // Just update this segment.
|
if (auto nSegments = size_t(ceil(l / max_segment_length)); nSegments == 1) { // Just update this segment.
|
||||||
push_line_to_output(line, line.feedrate() * line.volumetric_correction_avg(), comment);
|
push_line_to_output(line_idx, line.feedrate() * line.volumetric_correction_avg(), comment);
|
||||||
} else {
|
} else {
|
||||||
bool accelerating = line.volumetric_extrusion_rate_start < line.volumetric_extrusion_rate_end;
|
bool accelerating = line.volumetric_extrusion_rate_start < line.volumetric_extrusion_rate_end;
|
||||||
// Update the initial and final feed rate values.
|
// Update the initial and final feed rate values.
|
||||||
@ -439,7 +457,7 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line)
|
|||||||
line.pos_end[i] = pos_start[i] + (pos_end[i] - pos_start[i]) * t;
|
line.pos_end[i] = pos_start[i] + (pos_end[i] - pos_start[i]) * t;
|
||||||
line.pos_provided[i] = true;
|
line.pos_provided[i] = true;
|
||||||
}
|
}
|
||||||
push_line_to_output(line, pos_start[4], comment);
|
push_line_to_output(line_idx, pos_start[4], comment);
|
||||||
comment = nullptr;
|
comment = nullptr;
|
||||||
|
|
||||||
float new_pos_start_feedrate = pos_start[4];
|
float new_pos_start_feedrate = pos_start[4];
|
||||||
@ -459,7 +477,7 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line)
|
|||||||
line.pos_provided[j] = true;
|
line.pos_provided[j] = true;
|
||||||
}
|
}
|
||||||
// Interpolate the feed rate at the center of the segment.
|
// Interpolate the feed rate at the center of the segment.
|
||||||
push_line_to_output(line, pos_start[4] + (pos_end[4] - pos_start[4]) * (float(i) - 0.5f) / float(nSegments), comment);
|
push_line_to_output(line_idx, pos_start[4] + (pos_end[4] - pos_start[4]) * (float(i) - 0.5f) / float(nSegments), comment);
|
||||||
comment = nullptr;
|
comment = nullptr;
|
||||||
memcpy(line.pos_start, line.pos_end, sizeof(float)*5);
|
memcpy(line.pos_start, line.pos_end, sizeof(float)*5);
|
||||||
}
|
}
|
||||||
@ -468,13 +486,13 @@ void PressureEqualizer::output_gcode_line(GCodeLine &line)
|
|||||||
line.pos_end[i] = pos_end2[i];
|
line.pos_end[i] = pos_end2[i];
|
||||||
line.pos_provided[i] = true;
|
line.pos_provided[i] = true;
|
||||||
}
|
}
|
||||||
push_line_to_output(line, pos_end[4], comment);
|
push_line_to_output(line_idx, pos_end[4], comment);
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0; i < 4; ++ i) {
|
for (int i = 0; i < 4; ++ i) {
|
||||||
line.pos_end[i] = pos_end[i];
|
line.pos_end[i] = pos_end[i];
|
||||||
line.pos_provided[i] = true;
|
line.pos_provided[i] = true;
|
||||||
}
|
}
|
||||||
push_line_to_output(line, pos_end[4], comment);
|
push_line_to_output(line_idx, pos_end[4], comment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -501,6 +519,11 @@ void PressureEqualizer::adjust_volumetric_rate()
|
|||||||
for (; !m_gcode_lines[idx_prev].extruding() && idx_prev != fist_line_idx; --idx_prev);
|
for (; !m_gcode_lines[idx_prev].extruding() && idx_prev != fist_line_idx; --idx_prev);
|
||||||
if (!m_gcode_lines[idx_prev].extruding())
|
if (!m_gcode_lines[idx_prev].extruding())
|
||||||
break;
|
break;
|
||||||
|
// Don't decelerate before ironing and gap-fill.
|
||||||
|
if (m_gcode_lines[line_idx].extrusion_role == erIroning || m_gcode_lines[line_idx].extrusion_role == erGapFill) {
|
||||||
|
line_idx = idx_prev;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// Volumetric extrusion rate at the start of the succeding segment.
|
// Volumetric extrusion rate at the start of the succeding segment.
|
||||||
float rate_succ = m_gcode_lines[line_idx].volumetric_extrusion_rate_start;
|
float rate_succ = m_gcode_lines[line_idx].volumetric_extrusion_rate_start;
|
||||||
// What is the gradient of the extrusion rate between idx_prev and idx?
|
// What is the gradient of the extrusion rate between idx_prev and idx?
|
||||||
@ -517,7 +540,7 @@ void PressureEqualizer::adjust_volumetric_rate()
|
|||||||
// Limit by the succeeding volumetric flow rate.
|
// Limit by the succeeding volumetric flow rate.
|
||||||
rate_end = rate_succ;
|
rate_end = rate_succ;
|
||||||
|
|
||||||
if (line.extrusion_role == erExternalPerimeter || line.extrusion_role == erGapFill || line.extrusion_role == erBridgeInfill || line.extrusion_role == erIroning) {
|
if (!line.adjustable_flow || line.extrusion_role == erExternalPerimeter || line.extrusion_role == erGapFill || line.extrusion_role == erBridgeInfill || line.extrusion_role == erIroning) {
|
||||||
rate_end = line.volumetric_extrusion_rate_end;
|
rate_end = line.volumetric_extrusion_rate_end;
|
||||||
} else if (line.volumetric_extrusion_rate_end > rate_end) {
|
} else if (line.volumetric_extrusion_rate_end > rate_end) {
|
||||||
line.volumetric_extrusion_rate_end = rate_end;
|
line.volumetric_extrusion_rate_end = rate_end;
|
||||||
@ -529,16 +552,20 @@ void PressureEqualizer::adjust_volumetric_rate()
|
|||||||
// Use the original, 'floating' extrusion rate as a starting point for the limiter.
|
// Use the original, 'floating' extrusion rate as a starting point for the limiter.
|
||||||
}
|
}
|
||||||
|
|
||||||
float rate_start = rate_end + rate_slope * line.time_corrected();
|
if (line.adjustable_flow) {
|
||||||
if (rate_start < line.volumetric_extrusion_rate_start) {
|
float rate_start = rate_end + rate_slope * line.time_corrected();
|
||||||
// Limit the volumetric extrusion rate at the start of this segment due to a segment
|
if (rate_start < line.volumetric_extrusion_rate_start) {
|
||||||
// of ExtrusionType iRole, which will be extruded in the future.
|
// Limit the volumetric extrusion rate at the start of this segment due to a segment
|
||||||
line.volumetric_extrusion_rate_start = rate_start;
|
// of ExtrusionType iRole, which will be extruded in the future.
|
||||||
line.max_volumetric_extrusion_rate_slope_negative = rate_slope;
|
line.volumetric_extrusion_rate_start = rate_start;
|
||||||
line.modified = true;
|
line.max_volumetric_extrusion_rate_slope_negative = rate_slope;
|
||||||
|
line.modified = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// feedrate_per_extrusion_role[iRole] = (iRole == line.extrusion_role) ? line.volumetric_extrusion_rate_start : rate_start;
|
// feedrate_per_extrusion_role[iRole] = (iRole == line.extrusion_role) ? line.volumetric_extrusion_rate_start : rate_start;
|
||||||
feedrate_per_extrusion_role[iRole] = line.volumetric_extrusion_rate_start;
|
// Don't store feed rate for ironing and gap-fill.
|
||||||
|
if (line.extrusion_role != erIroning && line.extrusion_role != erGapFill)
|
||||||
|
feedrate_per_extrusion_role[iRole] = line.volumetric_extrusion_rate_start;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -551,6 +578,11 @@ void PressureEqualizer::adjust_volumetric_rate()
|
|||||||
for (; !m_gcode_lines[idx_next].extruding() && idx_next != last_line_idx; ++idx_next);
|
for (; !m_gcode_lines[idx_next].extruding() && idx_next != last_line_idx; ++idx_next);
|
||||||
if (!m_gcode_lines[idx_next].extruding())
|
if (!m_gcode_lines[idx_next].extruding())
|
||||||
break;
|
break;
|
||||||
|
// Don't accelerate after ironing and gap-fill.
|
||||||
|
if (m_gcode_lines[line_idx].extrusion_role == erIroning || m_gcode_lines[line_idx].extrusion_role == erGapFill) {
|
||||||
|
line_idx = idx_next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
float rate_prec = m_gcode_lines[line_idx].volumetric_extrusion_rate_end;
|
float rate_prec = m_gcode_lines[line_idx].volumetric_extrusion_rate_end;
|
||||||
// What is the gradient of the extrusion rate between idx_prev and idx?
|
// What is the gradient of the extrusion rate between idx_prev and idx?
|
||||||
line_idx = idx_next;
|
line_idx = idx_next;
|
||||||
@ -562,7 +594,7 @@ void PressureEqualizer::adjust_volumetric_rate()
|
|||||||
continue; // The positive rate is unlimited or the rate for ExtrusionRole iRole is unlimited.
|
continue; // The positive rate is unlimited or the rate for ExtrusionRole iRole is unlimited.
|
||||||
|
|
||||||
float rate_start = feedrate_per_extrusion_role[iRole];
|
float rate_start = feedrate_per_extrusion_role[iRole];
|
||||||
if (line.extrusion_role == erExternalPerimeter || line.extrusion_role == erGapFill || line.extrusion_role == erBridgeInfill || line.extrusion_role == erIroning) {
|
if (!line.adjustable_flow || line.extrusion_role == erExternalPerimeter || line.extrusion_role == erGapFill || line.extrusion_role == erBridgeInfill || line.extrusion_role == erIroning) {
|
||||||
rate_start = line.volumetric_extrusion_rate_start;
|
rate_start = line.volumetric_extrusion_rate_start;
|
||||||
} else if (iRole == line.extrusion_role && rate_prec < rate_start)
|
} else if (iRole == line.extrusion_role && rate_prec < rate_start)
|
||||||
rate_start = rate_prec;
|
rate_start = rate_prec;
|
||||||
@ -575,16 +607,21 @@ void PressureEqualizer::adjust_volumetric_rate()
|
|||||||
} else {
|
} else {
|
||||||
// Use the original, 'floating' extrusion rate as a starting point for the limiter.
|
// Use the original, 'floating' extrusion rate as a starting point for the limiter.
|
||||||
}
|
}
|
||||||
float rate_end = rate_start + rate_slope * line.time_corrected();
|
|
||||||
if (rate_end < line.volumetric_extrusion_rate_end) {
|
if (line.adjustable_flow) {
|
||||||
// Limit the volumetric extrusion rate at the start of this segment due to a segment
|
float rate_end = rate_start + rate_slope * line.time_corrected();
|
||||||
// of ExtrusionType iRole, which was extruded before.
|
if (rate_end < line.volumetric_extrusion_rate_end) {
|
||||||
line.volumetric_extrusion_rate_end = rate_end;
|
// Limit the volumetric extrusion rate at the start of this segment due to a segment
|
||||||
line.max_volumetric_extrusion_rate_slope_positive = rate_slope;
|
// of ExtrusionType iRole, which was extruded before.
|
||||||
line.modified = true;
|
line.volumetric_extrusion_rate_end = rate_end;
|
||||||
|
line.max_volumetric_extrusion_rate_slope_positive = rate_slope;
|
||||||
|
line.modified = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// feedrate_per_extrusion_role[iRole] = (iRole == line.extrusion_role) ? line.volumetric_extrusion_rate_end : rate_end;
|
// feedrate_per_extrusion_role[iRole] = (iRole == line.extrusion_role) ? line.volumetric_extrusion_rate_end : rate_end;
|
||||||
feedrate_per_extrusion_role[iRole] = line.volumetric_extrusion_rate_end;
|
// Don't store feed rate for ironing and gap-fill.
|
||||||
|
if (line.extrusion_role != erIroning && line.extrusion_role != erGapFill)
|
||||||
|
feedrate_per_extrusion_role[iRole] = line.volumetric_extrusion_rate_end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -624,6 +661,7 @@ inline void PressureEqualizer::push_to_output(const char *text, const size_t len
|
|||||||
// Copy the text to the output.
|
// Copy the text to the output.
|
||||||
if (len != 0) {
|
if (len != 0) {
|
||||||
memcpy(output_buffer.data() + output_buffer_length, text, len);
|
memcpy(output_buffer.data() + output_buffer_length, text, len);
|
||||||
|
this->output_buffer_prev_length = this->output_buffer_length;
|
||||||
output_buffer_length += len;
|
output_buffer_length += len;
|
||||||
}
|
}
|
||||||
if (add_eol)
|
if (add_eol)
|
||||||
@ -631,9 +669,22 @@ inline void PressureEqualizer::push_to_output(const char *text, const size_t len
|
|||||||
output_buffer[output_buffer_length] = 0;
|
output_buffer[output_buffer_length] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PressureEqualizer::push_line_to_output(const GCodeLine &line, const float new_feedrate, const char *comment)
|
inline bool PressureEqualizer::is_just_feedrate_provided(const GCodeLine &line)
|
||||||
{
|
{
|
||||||
push_to_output(EXTRUDE_END_TAG.data(), EXTRUDE_END_TAG.length(), true);
|
return line.pos_provided[4] && !line.pos_provided[0] && !line.pos_provided[1] && !line.pos_provided[2] && !line.pos_provided[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
void PressureEqualizer::push_line_to_output(const size_t line_idx, const float new_feedrate, const char *comment)
|
||||||
|
{
|
||||||
|
const GCodeLine &line = this->m_gcode_lines[line_idx];
|
||||||
|
if (line_idx > 0) {
|
||||||
|
const GCodeLine &prev_line = this->m_gcode_lines[line_idx - 1];
|
||||||
|
if (prev_line.extrude_set_speed_tag && this->is_just_feedrate_provided(prev_line))
|
||||||
|
this->output_buffer_length = this->output_buffer_prev_length; // Remove the last line because it only sets the speed for an empty block of g-code lines, so it is useless.
|
||||||
|
else
|
||||||
|
push_to_output(EXTRUDE_END_TAG.data(), EXTRUDE_END_TAG.length(), true);
|
||||||
|
} else
|
||||||
|
push_to_output(EXTRUDE_END_TAG.data(), EXTRUDE_END_TAG.length(), true);
|
||||||
|
|
||||||
GCodeG1Formatter feedrate_formatter;
|
GCodeG1Formatter feedrate_formatter;
|
||||||
feedrate_formatter.emit_f(new_feedrate);
|
feedrate_formatter.emit_f(new_feedrate);
|
||||||
|
@ -81,6 +81,10 @@ private:
|
|||||||
bool m_retracted;
|
bool m_retracted;
|
||||||
bool m_use_relative_e_distances;
|
bool m_use_relative_e_distances;
|
||||||
|
|
||||||
|
// Indicate if extrude set speed block was opened using the tag ";_EXTRUDE_SET_SPEED"
|
||||||
|
// or not (not opened, or it was closed using the tag ";_EXTRUDE_END").
|
||||||
|
bool opened_extrude_set_speed_block = false;
|
||||||
|
|
||||||
enum GCodeLineType {
|
enum GCodeLineType {
|
||||||
GCODELINETYPE_INVALID,
|
GCODELINETYPE_INVALID,
|
||||||
GCODELINETYPE_NOOP,
|
GCODELINETYPE_NOOP,
|
||||||
@ -139,7 +143,7 @@ private:
|
|||||||
// X,Y,Z,E,F. Storing the state of the currently active extruder only.
|
// X,Y,Z,E,F. Storing the state of the currently active extruder only.
|
||||||
float pos_start[5];
|
float pos_start[5];
|
||||||
float pos_end[5];
|
float pos_end[5];
|
||||||
// Was the axis found on the G-code line? X,Y,Z,F
|
// Was the axis found on the G-code line? X,Y,Z,E,F
|
||||||
bool pos_provided[5];
|
bool pos_provided[5];
|
||||||
|
|
||||||
// Index of the active extruder.
|
// Index of the active extruder.
|
||||||
@ -158,11 +162,17 @@ private:
|
|||||||
// If set to zero, the slope is unlimited.
|
// If set to zero, the slope is unlimited.
|
||||||
float max_volumetric_extrusion_rate_slope_positive;
|
float max_volumetric_extrusion_rate_slope_positive;
|
||||||
float max_volumetric_extrusion_rate_slope_negative;
|
float max_volumetric_extrusion_rate_slope_negative;
|
||||||
|
|
||||||
|
bool adjustable_flow = false;
|
||||||
|
|
||||||
|
bool extrude_set_speed_tag = false;
|
||||||
|
bool extrude_end_tag = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Output buffer will only grow. It will not be reallocated over and over.
|
// Output buffer will only grow. It will not be reallocated over and over.
|
||||||
std::vector<char> output_buffer;
|
std::vector<char> output_buffer;
|
||||||
size_t output_buffer_length;
|
size_t output_buffer_length;
|
||||||
|
size_t output_buffer_prev_length;
|
||||||
|
|
||||||
#ifdef PRESSURE_EQUALIZER_DEBUG
|
#ifdef PRESSURE_EQUALIZER_DEBUG
|
||||||
// For debugging purposes. Index of the G-code line processed.
|
// For debugging purposes. Index of the G-code line processed.
|
||||||
@ -170,7 +180,7 @@ private:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool process_line(const char *line, const char *line_end, GCodeLine &buf);
|
bool process_line(const char *line, const char *line_end, GCodeLine &buf);
|
||||||
void output_gcode_line(GCodeLine &buf);
|
void output_gcode_line(size_t line_idx);
|
||||||
|
|
||||||
// Go back from the current circular_buffer_pos and lower the feedtrate to decrease the slope of the extrusion rate changes.
|
// Go back from the current circular_buffer_pos and lower the feedtrate to decrease the slope of the extrusion rate changes.
|
||||||
// Then go forward and adjust the feedrate to decrease the slope of the extrusion rate changes.
|
// Then go forward and adjust the feedrate to decrease the slope of the extrusion rate changes.
|
||||||
@ -181,7 +191,9 @@ private:
|
|||||||
inline void push_to_output(const std::string &text, bool add_eol);
|
inline void push_to_output(const std::string &text, bool add_eol);
|
||||||
inline void push_to_output(const char *text, size_t len, bool add_eol = true);
|
inline void push_to_output(const char *text, size_t len, bool add_eol = true);
|
||||||
// Push a G-code line to the output.
|
// Push a G-code line to the output.
|
||||||
void push_line_to_output(const GCodeLine &line, float new_feedrate, const char *comment);
|
void push_line_to_output(size_t line_idx, float new_feedrate, const char *comment);
|
||||||
|
|
||||||
|
inline bool is_just_feedrate_provided(const GCodeLine &line);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::queue<LayerResult*> m_layer_results;
|
std::queue<LayerResult*> m_layer_results;
|
||||||
|
@ -319,7 +319,7 @@ struct GlobalModelInfo {
|
|||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto compute_dist_to_plane = [](const Vec3f& position, const Vec3f& plane_origin, const Vec3f& plane_normal) {
|
auto compute_dist_to_plane = [](const Vec3f &position, const Vec3f &plane_origin, const Vec3f &plane_normal) {
|
||||||
Vec3f orig_to_point = position - plane_origin;
|
Vec3f orig_to_point = position - plane_origin;
|
||||||
return std::abs(orig_to_point.dot(plane_normal));
|
return std::abs(orig_to_point.dot(plane_normal));
|
||||||
};
|
};
|
||||||
@ -403,9 +403,9 @@ Polygons extract_perimeter_polygons(const Layer *layer, const SeamPosition confi
|
|||||||
if (ex_entity->is_collection()) { //collection of inner, outer, and overhang perimeters
|
if (ex_entity->is_collection()) { //collection of inner, outer, and overhang perimeters
|
||||||
for (const ExtrusionEntity *perimeter : static_cast<const ExtrusionEntityCollection*>(ex_entity)->entities) {
|
for (const ExtrusionEntity *perimeter : static_cast<const ExtrusionEntityCollection*>(ex_entity)->entities) {
|
||||||
ExtrusionRole role = perimeter->role();
|
ExtrusionRole role = perimeter->role();
|
||||||
if (perimeter->is_loop()){
|
if (perimeter->is_loop()) {
|
||||||
for (const ExtrusionPath& path : static_cast<const ExtrusionLoop*>(perimeter)->paths){
|
for (const ExtrusionPath &path : static_cast<const ExtrusionLoop*>(perimeter)->paths) {
|
||||||
if (path.role() == ExtrusionRole::erExternalPerimeter){
|
if (path.role() == ExtrusionRole::erExternalPerimeter) {
|
||||||
role = ExtrusionRole::erExternalPerimeter;
|
role = ExtrusionRole::erExternalPerimeter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -449,7 +449,7 @@ Polygons extract_perimeter_polygons(const Layer *layer, const SeamPosition confi
|
|||||||
//each SeamCandidate also contains pointer to shared Perimeter structure representing the polygon
|
//each SeamCandidate also contains pointer to shared Perimeter structure representing the polygon
|
||||||
// if Custom Seam modifiers are present, oversamples the polygon if necessary to better fit user intentions
|
// if Custom Seam modifiers are present, oversamples the polygon if necessary to better fit user intentions
|
||||||
void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const LayerRegion *region,
|
void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const LayerRegion *region,
|
||||||
bool arachne_generated, const GlobalModelInfo &global_model_info, PrintObjectSeamData::LayerSeams &result) {
|
const GlobalModelInfo &global_model_info, PrintObjectSeamData::LayerSeams &result) {
|
||||||
if (orig_polygon.size() == 0) {
|
if (orig_polygon.size() == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -464,26 +464,6 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const
|
|||||||
std::vector<float> polygon_angles = calculate_polygon_angles_at_vertices(polygon, lengths,
|
std::vector<float> polygon_angles = calculate_polygon_angles_at_vertices(polygon, lengths,
|
||||||
SeamPlacer::polygon_local_angles_arm_distance);
|
SeamPlacer::polygon_local_angles_arm_distance);
|
||||||
|
|
||||||
// resample smooth surfaces from arachne, so that alignment finds short path down, and does not create unnecesary curves
|
|
||||||
if (arachne_generated && std::all_of(polygon_angles.begin(), polygon_angles.end(), [](float angle) {
|
|
||||||
return compute_angle_penalty(angle) > SeamPlacer::sharp_angle_penalty_snapping_threshold;
|
|
||||||
})) {
|
|
||||||
float total_dist = std::accumulate(lengths.begin(), lengths.end(), 0.0f);
|
|
||||||
float avg_dist = total_dist / float(lengths.size());
|
|
||||||
if (avg_dist < SeamPlacer::seam_align_tolerable_dist * 2.0f){
|
|
||||||
coord_t sampling_dist = scaled(avg_dist*0.2f);
|
|
||||||
|
|
||||||
polygon.points = polygon.equally_spaced_points(sampling_dist);
|
|
||||||
lengths.clear();
|
|
||||||
for (size_t point_idx = 0; point_idx < polygon.size() - 1; ++point_idx) {
|
|
||||||
lengths.push_back((unscale(polygon[point_idx]) - unscale(polygon[point_idx + 1])).norm());
|
|
||||||
}
|
|
||||||
lengths.push_back(std::max((unscale(polygon[0]) - unscale(polygon[polygon.size() - 1])).norm(), 0.1));
|
|
||||||
polygon_angles = calculate_polygon_angles_at_vertices(polygon, lengths, avg_dist);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
result.perimeters.push_back( { });
|
result.perimeters.push_back( { });
|
||||||
Perimeter &perimeter = result.perimeters.back();
|
Perimeter &perimeter = result.perimeters.back();
|
||||||
|
|
||||||
@ -540,50 +520,72 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const
|
|||||||
result.points.emplace_back(position, perimeter, local_ccw_angle, type);
|
result.points.emplace_back(position, perimeter, local_ccw_angle, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
perimeter.end_index = result.points.size() - 1;
|
perimeter.end_index = result.points.size();
|
||||||
|
|
||||||
// We will find first patch of enforced points (patch: continuous section of enforced points) and select the middle
|
|
||||||
// point, which will have priority during alignment
|
|
||||||
// If there are multiple enforced patches in the perimeter, others are ignored
|
|
||||||
if (some_point_enforced) {
|
if (some_point_enforced) {
|
||||||
size_t perimeter_size = perimeter.end_index - perimeter.start_index + 1;
|
// We will patches of enforced points (patch: continuous section of enforced points), choose
|
||||||
|
// the longest patch, and select the middle point or sharp point (depending on the angle)
|
||||||
|
// this point will have high priority on this perimeter
|
||||||
|
size_t perimeter_size = perimeter.end_index - perimeter.start_index;
|
||||||
const auto next_index = [&](size_t idx) {
|
const auto next_index = [&](size_t idx) {
|
||||||
return perimeter.start_index + Slic3r::next_idx_modulo(idx - perimeter.start_index, perimeter_size);
|
return perimeter.start_index + Slic3r::next_idx_modulo(idx - perimeter.start_index, perimeter_size);
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t first_enforced_idx = perimeter.start_index;
|
std::vector<size_t> patches_starts_ends;
|
||||||
for (size_t _ = 0; _ < perimeter_size; ++_) {
|
for (size_t i = perimeter.start_index; i < perimeter.end_index; ++i) {
|
||||||
if (result.points[first_enforced_idx].type != EnforcedBlockedSeamPoint::Enforced &&
|
if (result.points[i].type != EnforcedBlockedSeamPoint::Enforced &&
|
||||||
result.points[next_index(first_enforced_idx)].type == EnforcedBlockedSeamPoint::Enforced) {
|
result.points[next_index(i)].type == EnforcedBlockedSeamPoint::Enforced) {
|
||||||
break;
|
patches_starts_ends.push_back(next_index(i));
|
||||||
|
}
|
||||||
|
if (result.points[i].type == EnforcedBlockedSeamPoint::Enforced &&
|
||||||
|
result.points[next_index(i)].type != EnforcedBlockedSeamPoint::Enforced) {
|
||||||
|
patches_starts_ends.push_back(next_index(i));
|
||||||
}
|
}
|
||||||
first_enforced_idx = next_index(first_enforced_idx);
|
|
||||||
}
|
}
|
||||||
first_enforced_idx = next_index(first_enforced_idx);
|
//if patches_starts_ends are empty, it means that the whole perimeter is enforced.. don't do anything in that case
|
||||||
|
if (!patches_starts_ends.empty()) {
|
||||||
// Gather also points with large angles (these are points from the original mesh, since oversampled points have zero angle)
|
//if the first point in the patches is not enforced, it marks a patch end. in that case, put it to the end and start on next
|
||||||
// If there are any, the middle point will be picked from those (makes drawing over sharp corners easier)
|
// to simplify the processing
|
||||||
std::vector<size_t> orig_large_angle_points_indices { };
|
assert(patches_starts_ends.size() % 2 == 0);
|
||||||
std::vector<size_t> viable_points_indices { };
|
bool start_on_second = false;
|
||||||
size_t last_enforced_idx = first_enforced_idx;
|
if (result.points[patches_starts_ends[0]].type != EnforcedBlockedSeamPoint::Enforced) {
|
||||||
for (size_t _ = 0; _ < perimeter_size; ++_) {
|
start_on_second = true;
|
||||||
if (result.points[last_enforced_idx].type != EnforcedBlockedSeamPoint::Enforced) {
|
patches_starts_ends.push_back(patches_starts_ends[0]);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
viable_points_indices.push_back(last_enforced_idx);
|
//now pick the longest patch
|
||||||
if (compute_angle_penalty(result.points[last_enforced_idx].local_ccw_angle)
|
std::pair<size_t, size_t> longest_patch { 0, 0 };
|
||||||
< SeamPlacer::sharp_angle_penalty_snapping_threshold) {
|
auto patch_len = [perimeter_size](const std::pair<size_t, size_t> &start_end) {
|
||||||
orig_large_angle_points_indices.push_back(last_enforced_idx);
|
if (start_end.second < start_end.first) {
|
||||||
|
return start_end.first + (perimeter_size - start_end.second);
|
||||||
|
} else {
|
||||||
|
return start_end.second - start_end.first;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for (size_t patch_idx = start_on_second ? 1 : 0; patch_idx < patches_starts_ends.size(); patch_idx += 2) {
|
||||||
|
std::pair<size_t, size_t> current_patch { patches_starts_ends[patch_idx], patches_starts_ends[patch_idx
|
||||||
|
+ 1] };
|
||||||
|
if (patch_len(longest_patch) < patch_len(current_patch)) {
|
||||||
|
longest_patch = current_patch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::vector<size_t> viable_points_indices;
|
||||||
|
std::vector<size_t> large_angle_points_indices;
|
||||||
|
for (size_t point_idx = longest_patch.first; point_idx != longest_patch.second;
|
||||||
|
point_idx = next_index(point_idx)) {
|
||||||
|
viable_points_indices.push_back(point_idx);
|
||||||
|
if (std::abs(result.points[point_idx].local_ccw_angle)
|
||||||
|
> SeamPlacer::sharp_angle_snapping_threshold) {
|
||||||
|
large_angle_points_indices.push_back(point_idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(viable_points_indices.size() > 0);
|
||||||
|
if (large_angle_points_indices.empty()) {
|
||||||
|
size_t central_idx = viable_points_indices[viable_points_indices.size() / 2];
|
||||||
|
result.points[central_idx].central_enforcer = true;
|
||||||
|
} else {
|
||||||
|
size_t central_idx = large_angle_points_indices.size() / 2;
|
||||||
|
result.points[large_angle_points_indices[central_idx]].central_enforcer = true;
|
||||||
}
|
}
|
||||||
last_enforced_idx = next_index(last_enforced_idx);
|
|
||||||
}
|
|
||||||
assert(viable_points_indices.size() > 0);
|
|
||||||
if (orig_large_angle_points_indices.empty()) {
|
|
||||||
size_t central_idx = viable_points_indices[viable_points_indices.size() / 2];
|
|
||||||
result.points[central_idx].central_enforcer = true;
|
|
||||||
} else {
|
|
||||||
size_t central_idx = orig_large_angle_points_indices.size() / 2;
|
|
||||||
result.points[orig_large_angle_points_indices[central_idx]].central_enforcer = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -603,7 +605,7 @@ std::pair<size_t, size_t> find_previous_and_next_perimeter_point(const std::vect
|
|||||||
prev = current.perimeter.end_index;
|
prev = current.perimeter.end_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (point_index == current.perimeter.end_index) {
|
if (point_index == current.perimeter.end_index - 1) {
|
||||||
// if point_index is equal to end, than next neighbour is at the start
|
// if point_index is equal to end, than next neighbour is at the start
|
||||||
next = current.perimeter.start_index;
|
next = current.perimeter.start_index;
|
||||||
}
|
}
|
||||||
@ -731,7 +733,8 @@ struct SeamComparator {
|
|||||||
float angle_importance;
|
float angle_importance;
|
||||||
explicit SeamComparator(SeamPosition setup) :
|
explicit SeamComparator(SeamPosition setup) :
|
||||||
setup(setup) {
|
setup(setup) {
|
||||||
angle_importance = setup == spNearest ? SeamPlacer::angle_importance_nearest : SeamPlacer::angle_importance_aligned;
|
angle_importance =
|
||||||
|
setup == spNearest ? SeamPlacer::angle_importance_nearest : SeamPlacer::angle_importance_aligned;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Standard comparator, must respect the requirements of comparators (e.g. give same result on same inputs) for sorting usage
|
// Standard comparator, must respect the requirements of comparators (e.g. give same result on same inputs) for sorting usage
|
||||||
@ -748,8 +751,7 @@ struct SeamComparator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//avoid overhangs
|
//avoid overhangs
|
||||||
if (a.overhang > SeamPlacer::overhang_distance_tolerance_factor * a.perimeter.flow_width ||
|
if (a.overhang > 0.0f || b.overhang > 0.0f) {
|
||||||
b.overhang > SeamPlacer::overhang_distance_tolerance_factor * b.perimeter.flow_width) {
|
|
||||||
return a.overhang < b.overhang;
|
return a.overhang < b.overhang;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -773,10 +775,10 @@ struct SeamComparator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// the penalites are kept close to range [0-1.x] however, it should not be relied upon
|
// the penalites are kept close to range [0-1.x] however, it should not be relied upon
|
||||||
float penalty_a = a.visibility +
|
float penalty_a = a.overhang + a.visibility +
|
||||||
angle_importance * compute_angle_penalty(a.local_ccw_angle)
|
angle_importance * compute_angle_penalty(a.local_ccw_angle)
|
||||||
+ distance_penalty_a;
|
+ distance_penalty_a;
|
||||||
float penalty_b = b.visibility +
|
float penalty_b = b.overhang + b.visibility +
|
||||||
angle_importance * compute_angle_penalty(b.local_ccw_angle)
|
angle_importance * compute_angle_penalty(b.local_ccw_angle)
|
||||||
+ distance_penalty_b;
|
+ distance_penalty_b;
|
||||||
|
|
||||||
@ -806,8 +808,8 @@ struct SeamComparator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//avoid overhangs
|
//avoid overhangs
|
||||||
if (a.overhang > SeamPlacer::overhang_distance_tolerance_factor * a.perimeter.flow_width ||
|
if ((a.overhang > 0.0f || b.overhang > 0.0f)
|
||||||
b.overhang > SeamPlacer::overhang_distance_tolerance_factor * b.perimeter.flow_width) {
|
&& abs(a.overhang - b.overhang) > (0.1f * a.perimeter.flow_width)) {
|
||||||
return a.overhang < b.overhang;
|
return a.overhang < b.overhang;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -827,9 +829,9 @@ struct SeamComparator {
|
|||||||
return a.position.y() + SeamPlacer::seam_align_score_tolerance * 5.0f > b.position.y();
|
return a.position.y() + SeamPlacer::seam_align_score_tolerance * 5.0f > b.position.y();
|
||||||
}
|
}
|
||||||
|
|
||||||
float penalty_a = a.visibility
|
float penalty_a = a.overhang + a.visibility
|
||||||
+ angle_importance * compute_angle_penalty(a.local_ccw_angle);
|
+ angle_importance * compute_angle_penalty(a.local_ccw_angle);
|
||||||
float penalty_b = b.visibility +
|
float penalty_b = b.overhang + b.visibility +
|
||||||
angle_importance * compute_angle_penalty(b.local_ccw_angle);
|
angle_importance * compute_angle_penalty(b.local_ccw_angle);
|
||||||
|
|
||||||
return penalty_a <= penalty_b || penalty_a - penalty_b < SeamPlacer::seam_align_score_tolerance;
|
return penalty_a <= penalty_b || penalty_a - penalty_b < SeamPlacer::seam_align_score_tolerance;
|
||||||
@ -838,13 +840,6 @@ struct SeamComparator {
|
|||||||
bool are_similar(const SeamCandidate &a, const SeamCandidate &b) const {
|
bool are_similar(const SeamCandidate &a, const SeamCandidate &b) const {
|
||||||
return is_first_not_much_worse(a, b) && is_first_not_much_worse(b, a);
|
return is_first_not_much_worse(a, b) && is_first_not_much_worse(b, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
float weight(const SeamCandidate &a) const {
|
|
||||||
if (setup == SeamPosition::spAligned && a.central_enforcer) {
|
|
||||||
return 2.0f;
|
|
||||||
}
|
|
||||||
return a.visibility + angle_importance * compute_angle_penalty(a.local_ccw_angle) / (1.0f + angle_importance);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef DEBUG_FILES
|
#ifdef DEBUG_FILES
|
||||||
@ -868,8 +863,8 @@ void debug_export_points(const std::vector<PrintObjectSeamData::LayerSeams> &lay
|
|||||||
min_vis = std::min(min_vis, point.visibility);
|
min_vis = std::min(min_vis, point.visibility);
|
||||||
max_vis = std::max(max_vis, point.visibility);
|
max_vis = std::max(max_vis, point.visibility);
|
||||||
|
|
||||||
min_weight = std::min(min_weight, -comparator.compute_angle_penalty(point.local_ccw_angle));
|
min_weight = std::min(min_weight, -compute_angle_penalty(point.local_ccw_angle));
|
||||||
max_weight = std::max(max_weight, -comparator.compute_angle_penalty(point.local_ccw_angle));
|
max_weight = std::max(max_weight, -compute_angle_penalty(point.local_ccw_angle));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -890,7 +885,7 @@ void debug_export_points(const std::vector<PrintObjectSeamData::LayerSeams> &lay
|
|||||||
visibility_svg.draw(scaled(Vec2f(point.position.head<2>())), visibility_fill);
|
visibility_svg.draw(scaled(Vec2f(point.position.head<2>())), visibility_fill);
|
||||||
|
|
||||||
Vec3i weight_color = value_to_rgbi(min_weight, max_weight,
|
Vec3i weight_color = value_to_rgbi(min_weight, max_weight,
|
||||||
-comparator.compute_angle_penalty(point.local_ccw_angle));
|
-compute_angle_penalty(point.local_ccw_angle));
|
||||||
std::string weight_fill = "rgb(" + std::to_string(weight_color.x()) + "," + std::to_string(weight_color.y())
|
std::string weight_fill = "rgb(" + std::to_string(weight_color.x()) + "," + std::to_string(weight_color.y())
|
||||||
+ ","
|
+ ","
|
||||||
+ std::to_string(weight_color.z()) + ")";
|
+ std::to_string(weight_color.z()) + ")";
|
||||||
@ -913,7 +908,7 @@ void pick_seam_point(std::vector<SeamCandidate> &perimeter_points, size_t start_
|
|||||||
size_t end_index = perimeter_points[start_index].perimeter.end_index;
|
size_t end_index = perimeter_points[start_index].perimeter.end_index;
|
||||||
|
|
||||||
size_t seam_index = start_index;
|
size_t seam_index = start_index;
|
||||||
for (size_t index = start_index; index <= end_index; ++index) {
|
for (size_t index = start_index; index < end_index; ++index) {
|
||||||
if (comparator.is_first_better(perimeter_points[index], perimeter_points[seam_index])) {
|
if (comparator.is_first_better(perimeter_points[index], perimeter_points[seam_index])) {
|
||||||
seam_index = index;
|
seam_index = index;
|
||||||
}
|
}
|
||||||
@ -927,7 +922,7 @@ size_t pick_nearest_seam_point_index(const std::vector<SeamCandidate> &perimeter
|
|||||||
SeamComparator comparator { spNearest };
|
SeamComparator comparator { spNearest };
|
||||||
|
|
||||||
size_t seam_index = start_index;
|
size_t seam_index = start_index;
|
||||||
for (size_t index = start_index; index <= end_index; ++index) {
|
for (size_t index = start_index; index < end_index; ++index) {
|
||||||
if (comparator.is_first_better(perimeter_points[index], perimeter_points[seam_index], preffered_location)) {
|
if (comparator.is_first_better(perimeter_points[index], perimeter_points[seam_index], preffered_location)) {
|
||||||
seam_index = index;
|
seam_index = index;
|
||||||
}
|
}
|
||||||
@ -954,10 +949,10 @@ void pick_random_seam_point(const std::vector<SeamCandidate> &perimeter_points,
|
|||||||
};
|
};
|
||||||
std::vector<Viable> viables;
|
std::vector<Viable> viables;
|
||||||
|
|
||||||
for (size_t index = start_index; index <= end_index; ++index) {
|
for (size_t index = start_index; index < end_index; ++index) {
|
||||||
if (comparator.are_similar(perimeter_points[index], perimeter_points[viable_example_index])) {
|
if (comparator.are_similar(perimeter_points[index], perimeter_points[viable_example_index])) {
|
||||||
// index ok, push info into viables
|
// index ok, push info into viables
|
||||||
Vec3f edge_to_next { perimeter_points[index == end_index ? start_index : index + 1].position
|
Vec3f edge_to_next { perimeter_points[index == end_index - 1 ? start_index : index + 1].position
|
||||||
- perimeter_points[index].position };
|
- perimeter_points[index].position };
|
||||||
float dist_to_next = edge_to_next.norm();
|
float dist_to_next = edge_to_next.norm();
|
||||||
viables.push_back( { index, dist_to_next, edge_to_next });
|
viables.push_back( { index, dist_to_next, edge_to_next });
|
||||||
@ -970,7 +965,7 @@ void pick_random_seam_point(const std::vector<SeamCandidate> &perimeter_points,
|
|||||||
viable_example_index = index;
|
viable_example_index = index;
|
||||||
viables.clear();
|
viables.clear();
|
||||||
|
|
||||||
Vec3f edge_to_next = (perimeter_points[index == end_index ? start_index : index + 1].position
|
Vec3f edge_to_next = (perimeter_points[index == end_index - 1 ? start_index : index + 1].position
|
||||||
- perimeter_points[index].position);
|
- perimeter_points[index].position);
|
||||||
float dist_to_next = edge_to_next.norm();
|
float dist_to_next = edge_to_next.norm();
|
||||||
viables.push_back( { index, dist_to_next, edge_to_next });
|
viables.push_back( { index, dist_to_next, edge_to_next });
|
||||||
@ -1024,8 +1019,8 @@ public:
|
|||||||
|
|
||||||
float distance_from_perimeter(const Point &point) const {
|
float distance_from_perimeter(const Point &point) const {
|
||||||
Vec2d p = unscale(point);
|
Vec2d p = unscale(point);
|
||||||
size_t hit_idx_out;
|
size_t hit_idx_out { };
|
||||||
Vec2d hit_point_out;
|
Vec2d hit_point_out = Vec2d::Zero();
|
||||||
auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, p, hit_idx_out, hit_point_out);
|
auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, p, hit_idx_out, hit_point_out);
|
||||||
if (distance < 0) {
|
if (distance < 0) {
|
||||||
return std::numeric_limits<float>::max();
|
return std::numeric_limits<float>::max();
|
||||||
@ -1051,14 +1046,12 @@ public:
|
|||||||
void SeamPlacer::gather_seam_candidates(const PrintObject *po,
|
void SeamPlacer::gather_seam_candidates(const PrintObject *po,
|
||||||
const SeamPlacerImpl::GlobalModelInfo &global_model_info, const SeamPosition configured_seam_preference) {
|
const SeamPlacerImpl::GlobalModelInfo &global_model_info, const SeamPosition configured_seam_preference) {
|
||||||
using namespace SeamPlacerImpl;
|
using namespace SeamPlacerImpl;
|
||||||
bool arachne_generated = po->config().perimeter_generator == PerimeterGeneratorType::Arachne;
|
|
||||||
|
|
||||||
PrintObjectSeamData &seam_data = m_seam_per_object.emplace(po, PrintObjectSeamData { }).first->second;
|
PrintObjectSeamData &seam_data = m_seam_per_object.emplace(po, PrintObjectSeamData { }).first->second;
|
||||||
seam_data.layers.resize(po->layer_count());
|
seam_data.layers.resize(po->layer_count());
|
||||||
|
|
||||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, po->layers().size()),
|
tbb::parallel_for(tbb::blocked_range<size_t>(0, po->layers().size()),
|
||||||
[po, configured_seam_preference, arachne_generated, &global_model_info, &seam_data]
|
[po, configured_seam_preference, &global_model_info, &seam_data]
|
||||||
(tbb::blocked_range<size_t> r) {
|
(tbb::blocked_range<size_t> r) {
|
||||||
for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) {
|
for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) {
|
||||||
PrintObjectSeamData::LayerSeams &layer_seams = seam_data.layers[layer_idx];
|
PrintObjectSeamData::LayerSeams &layer_seams = seam_data.layers[layer_idx];
|
||||||
const Layer *layer = po->get_layer(layer_idx);
|
const Layer *layer = po->get_layer(layer_idx);
|
||||||
@ -1068,7 +1061,7 @@ void SeamPlacer::gather_seam_candidates(const PrintObject *po,
|
|||||||
Polygons polygons = extract_perimeter_polygons(layer, configured_seam_preference, regions);
|
Polygons polygons = extract_perimeter_polygons(layer, configured_seam_preference, regions);
|
||||||
for (size_t poly_index = 0; poly_index < polygons.size(); ++poly_index) {
|
for (size_t poly_index = 0; poly_index < polygons.size(); ++poly_index) {
|
||||||
process_perimeter_polygon(polygons[poly_index], unscaled_z,
|
process_perimeter_polygon(polygons[poly_index], unscaled_z,
|
||||||
regions[poly_index], arachne_generated, global_model_info, layer_seams);
|
regions[poly_index], global_model_info, layer_seams);
|
||||||
}
|
}
|
||||||
auto functor = SeamCandidateCoordinateFunctor { layer_seams.points };
|
auto functor = SeamCandidateCoordinateFunctor { layer_seams.points };
|
||||||
seam_data.layers[layer_idx].points_tree =
|
seam_data.layers[layer_idx].points_tree =
|
||||||
@ -1119,11 +1112,18 @@ void SeamPlacer::calculate_overhangs_and_layer_embedding(const PrintObject *po)
|
|||||||
for (SeamCandidate &perimeter_point : layers[layer_idx].points) {
|
for (SeamCandidate &perimeter_point : layers[layer_idx].points) {
|
||||||
Point point = Point::new_scale(Vec2f { perimeter_point.position.head<2>() });
|
Point point = Point::new_scale(Vec2f { perimeter_point.position.head<2>() });
|
||||||
if (prev_layer_distancer.get() != nullptr) {
|
if (prev_layer_distancer.get() != nullptr) {
|
||||||
perimeter_point.overhang = prev_layer_distancer->distance_from_perimeter(point);
|
perimeter_point.overhang = (prev_layer_distancer->distance_from_perimeter(point)
|
||||||
|
+ 0.5f * perimeter_point.perimeter.flow_width
|
||||||
|
- tan(SeamPlacer::overhang_angle_threshold)
|
||||||
|
* po->layers()[layer_idx]->height)
|
||||||
|
/ (3.0f * perimeter_point.perimeter.flow_width);
|
||||||
|
//NOTE disables the feature to place seams on slowly decreasing areas. Remove the following line to enable.
|
||||||
|
perimeter_point.overhang = perimeter_point.overhang < 0.0f ? 0.0f : perimeter_point.overhang;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (should_compute_layer_embedding) { // search for embedded perimeter points (points hidden inside the print ,e.g. multimaterial join, best position for seam)
|
if (should_compute_layer_embedding) { // search for embedded perimeter points (points hidden inside the print ,e.g. multimaterial join, best position for seam)
|
||||||
perimeter_point.embedded_distance = current_layer_distancer->distance_from_perimeter(point);
|
perimeter_point.embedded_distance = current_layer_distancer->distance_from_perimeter(point)
|
||||||
|
+ 0.5f * perimeter_point.perimeter.flow_width;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1142,7 +1142,7 @@ void SeamPlacer::calculate_overhangs_and_layer_embedding(const PrintObject *po)
|
|||||||
// Used by align_seam_points().
|
// Used by align_seam_points().
|
||||||
std::optional<std::pair<size_t, size_t>> SeamPlacer::find_next_seam_in_layer(
|
std::optional<std::pair<size_t, size_t>> SeamPlacer::find_next_seam_in_layer(
|
||||||
const std::vector<PrintObjectSeamData::LayerSeams> &layers,
|
const std::vector<PrintObjectSeamData::LayerSeams> &layers,
|
||||||
const Vec3f& projected_position,
|
const Vec3f &projected_position,
|
||||||
const size_t layer_idx, const float max_distance,
|
const size_t layer_idx, const float max_distance,
|
||||||
const SeamPlacerImpl::SeamComparator &comparator) const {
|
const SeamPlacerImpl::SeamComparator &comparator) const {
|
||||||
using namespace SeamPlacerImpl;
|
using namespace SeamPlacerImpl;
|
||||||
@ -1205,9 +1205,7 @@ std::optional<std::pair<size_t, size_t>> SeamPlacer::find_next_seam_in_layer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::pair<size_t, size_t>> SeamPlacer::find_seam_string(const PrintObject *po,
|
std::vector<std::pair<size_t, size_t>> SeamPlacer::find_seam_string(const PrintObject *po,
|
||||||
std::pair<size_t, size_t> start_seam, const SeamPlacerImpl::SeamComparator &comparator,
|
std::pair<size_t, size_t> start_seam, const SeamPlacerImpl::SeamComparator &comparator) const {
|
||||||
float& string_weight) const {
|
|
||||||
string_weight = 0.0f;
|
|
||||||
const std::vector<PrintObjectSeamData::LayerSeams> &layers = m_seam_per_object.find(po)->second.layers;
|
const std::vector<PrintObjectSeamData::LayerSeams> &layers = m_seam_per_object.find(po)->second.layers;
|
||||||
int layer_idx = start_seam.first;
|
int layer_idx = start_seam.first;
|
||||||
|
|
||||||
@ -1230,7 +1228,8 @@ std::vector<std::pair<size_t, size_t>> SeamPlacer::find_seam_string(const PrintO
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
float max_distance = SeamPlacer::seam_align_tolerable_dist;
|
float max_distance = SeamPlacer::seam_align_tolerable_dist_factor *
|
||||||
|
layers[start_seam.first].points[start_seam.second].perimeter.flow_width;
|
||||||
Vec3f prev_position = layers[prev_point_index.first].points[prev_point_index.second].position;
|
Vec3f prev_position = layers[prev_point_index.first].points[prev_point_index.second].position;
|
||||||
Vec3f projected_position = prev_position;
|
Vec3f projected_position = prev_position;
|
||||||
projected_position.z() = float(po->get_layer(next_layer)->slice_z);
|
projected_position.z() = float(po->get_layer(next_layer)->slice_z);
|
||||||
@ -1241,11 +1240,6 @@ std::vector<std::pair<size_t, size_t>> SeamPlacer::find_seam_string(const PrintO
|
|||||||
|
|
||||||
if (maybe_next_seam.has_value()) {
|
if (maybe_next_seam.has_value()) {
|
||||||
// For old macOS (pre 10.14), std::optional does not have .value() method, so the code is using operator*() instead.
|
// For old macOS (pre 10.14), std::optional does not have .value() method, so the code is using operator*() instead.
|
||||||
std::pair<size_t, size_t> next_seam_coords = maybe_next_seam.operator*();
|
|
||||||
const auto &next_seam = layers[next_seam_coords.first].points[next_seam_coords.second];
|
|
||||||
bool is_moved = next_seam.perimeter.seam_index != next_seam_coords.second;
|
|
||||||
string_weight += comparator.weight(next_seam) -
|
|
||||||
is_moved ? comparator.weight(layers[next_seam_coords.first].points[next_seam.perimeter.seam_index]) : 0.0f;
|
|
||||||
seam_string.push_back(maybe_next_seam.operator*());
|
seam_string.push_back(maybe_next_seam.operator*());
|
||||||
prev_point_index = seam_string.back();
|
prev_point_index = seam_string.back();
|
||||||
//String added, prev_point_index updated
|
//String added, prev_point_index updated
|
||||||
@ -1253,15 +1247,14 @@ std::vector<std::pair<size_t, size_t>> SeamPlacer::find_seam_string(const PrintO
|
|||||||
if (step == 1) {
|
if (step == 1) {
|
||||||
reverse_lookup_direction();
|
reverse_lookup_direction();
|
||||||
if (next_layer < 0) {
|
if (next_layer < 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
next_layer += step;
|
next_layer += step;
|
||||||
}
|
}
|
||||||
|
|
||||||
return seam_string;
|
return seam_string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1300,7 +1293,7 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl::
|
|||||||
size_t current_point_index = 0;
|
size_t current_point_index = 0;
|
||||||
while (current_point_index < layer_perimeter_points.size()) {
|
while (current_point_index < layer_perimeter_points.size()) {
|
||||||
seams.emplace_back(layer_idx, layer_perimeter_points[current_point_index].perimeter.seam_index);
|
seams.emplace_back(layer_idx, layer_perimeter_points[current_point_index].perimeter.seam_index);
|
||||||
current_point_index = layer_perimeter_points[current_point_index].perimeter.end_index + 1;
|
current_point_index = layer_perimeter_points[current_point_index].perimeter.end_index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1331,18 +1324,15 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl::
|
|||||||
// This perimeter is already aligned, skip seam
|
// This perimeter is already aligned, skip seam
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
float seam_string_weight;
|
seam_string = this->find_seam_string(po, { layer_idx, seam_index }, comparator);
|
||||||
seam_string = this->find_seam_string(po, { layer_idx, seam_index }, comparator, seam_string_weight);
|
|
||||||
size_t step_size = 1 + seam_string.size() / 20;
|
size_t step_size = 1 + seam_string.size() / 20;
|
||||||
for (size_t alternative_start = 0; alternative_start < seam_string.size(); alternative_start+=step_size) {
|
for (size_t alternative_start = 0; alternative_start < seam_string.size(); alternative_start += step_size) {
|
||||||
float alternative_seam_string_weight = 0;
|
|
||||||
size_t start_layer_idx = seam_string[alternative_start].first;
|
size_t start_layer_idx = seam_string[alternative_start].first;
|
||||||
size_t seam_idx = layers[start_layer_idx].points[seam_string[alternative_start].second].perimeter.seam_index;
|
size_t seam_idx =
|
||||||
alternative_seam_string = this->find_seam_string(po, std::pair<size_t,size_t>(start_layer_idx, seam_idx), comparator,
|
layers[start_layer_idx].points[seam_string[alternative_start].second].perimeter.seam_index;
|
||||||
alternative_seam_string_weight);
|
alternative_seam_string = this->find_seam_string(po,
|
||||||
if (alternative_seam_string.size() >= SeamPlacer::seam_align_minimum_string_seams &&
|
std::pair<size_t, size_t>(start_layer_idx, seam_idx), comparator);
|
||||||
alternative_seam_string_weight > seam_string_weight) {
|
if (alternative_seam_string.size() > seam_string.size()) {
|
||||||
seam_string_weight = alternative_seam_string_weight;
|
|
||||||
seam_string = std::move(alternative_seam_string);
|
seam_string = std::move(alternative_seam_string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1361,36 +1351,60 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl::
|
|||||||
//repeat the alignment for the current seam, since it could be skipped due to alternative path being aligned.
|
//repeat the alignment for the current seam, since it could be skipped due to alternative path being aligned.
|
||||||
global_index--;
|
global_index--;
|
||||||
|
|
||||||
// gather all positions of seams and their weights (weights are derived as negative penalty, they are made positive in next step)
|
// gather all positions of seams and their weights
|
||||||
observations.resize(seam_string.size());
|
observations.resize(seam_string.size());
|
||||||
observation_points.resize(seam_string.size());
|
observation_points.resize(seam_string.size());
|
||||||
weights.resize(seam_string.size());
|
weights.resize(seam_string.size());
|
||||||
|
|
||||||
|
auto angle_3d = [](const Vec3f& a, const Vec3f& b){
|
||||||
|
return std::abs(acosf(a.normalized().dot(b.normalized())));
|
||||||
|
};
|
||||||
|
|
||||||
|
auto angle_weight = [](float angle){
|
||||||
|
return 1.0f / (0.1f + compute_angle_penalty(angle));
|
||||||
|
};
|
||||||
|
|
||||||
//gather points positions and weights
|
//gather points positions and weights
|
||||||
float total_length = 0.0f;
|
float total_length = 0.0f;
|
||||||
Vec3f last_point_pos = layers[seam_string[0].first].points[seam_string[0].second].position;
|
Vec3f last_point_pos = layers[seam_string[0].first].points[seam_string[0].second].position;
|
||||||
for (size_t index = 0; index < seam_string.size(); ++index) {
|
for (size_t index = 0; index < seam_string.size(); ++index) {
|
||||||
Vec3f pos = layers[seam_string[index].first].points[seam_string[index].second].position;
|
const SeamCandidate ¤t = layers[seam_string[index].first].points[seam_string[index].second];
|
||||||
total_length += (last_point_pos - pos).norm();
|
float layer_angle = 0.0f;
|
||||||
last_point_pos = pos;
|
if (index > 0 && index < seam_string.size() - 1) {
|
||||||
observations[index] = pos.head<2>();
|
layer_angle = angle_3d(
|
||||||
observation_points[index] = pos.z();
|
current.position
|
||||||
weights[index] = comparator.weight(layers[seam_string[index].first].points[seam_string[index].second]);
|
- layers[seam_string[index - 1].first].points[seam_string[index - 1].second].position,
|
||||||
|
layers[seam_string[index + 1].first].points[seam_string[index + 1].second].position
|
||||||
|
- current.position
|
||||||
|
);
|
||||||
|
}
|
||||||
|
observations[index] = current.position.head<2>();
|
||||||
|
observation_points[index] = current.position.z();
|
||||||
|
weights[index] = angle_weight(current.local_ccw_angle);
|
||||||
|
float sign = layer_angle > 2.0 * std::abs(current.local_ccw_angle) ? -1.0f : 1.0f;
|
||||||
|
if (current.type == EnforcedBlockedSeamPoint::Enforced) {
|
||||||
|
sign = 1.0f;
|
||||||
|
weights[index] += 3.0f;
|
||||||
|
}
|
||||||
|
total_length += sign * (last_point_pos - current.position).norm();
|
||||||
|
last_point_pos = current.position;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Curve Fitting
|
// Curve Fitting
|
||||||
size_t number_of_segments = std::max(size_t(1),
|
size_t number_of_segments = std::max(size_t(1),
|
||||||
size_t(total_length / SeamPlacer::seam_align_mm_per_segment));
|
size_t(std::max(0.0f,total_length) / SeamPlacer::seam_align_mm_per_segment));
|
||||||
auto curve = Geometry::fit_cubic_bspline(observations, observation_points, weights, number_of_segments);
|
auto curve = Geometry::fit_cubic_bspline(observations, observation_points, weights, number_of_segments);
|
||||||
|
|
||||||
// Do alignment - compute fitted point for each point in the string from its Z coord, and store the position into
|
// Do alignment - compute fitted point for each point in the string from its Z coord, and store the position into
|
||||||
// Perimeter structure of the point; also set flag aligned to true
|
// Perimeter structure of the point; also set flag aligned to true
|
||||||
for (size_t index = 0; index < seam_string.size(); ++index) {
|
for (size_t index = 0; index < seam_string.size(); ++index) {
|
||||||
const auto &pair = seam_string[index];
|
const auto &pair = seam_string[index];
|
||||||
const float t =
|
float t = std::min(1.0f, std::abs(layers[pair.first].points[pair.second].local_ccw_angle)
|
||||||
compute_angle_penalty(layers[pair.first].points[pair.second].local_ccw_angle)
|
/ SeamPlacer::sharp_angle_snapping_threshold);
|
||||||
< SeamPlacer::sharp_angle_penalty_snapping_threshold
|
if (layers[pair.first].points[pair.second].type == EnforcedBlockedSeamPoint::Enforced){
|
||||||
? 0.8f : 0.0f;
|
t = std::max(0.7f, t);
|
||||||
|
}
|
||||||
|
|
||||||
Vec3f current_pos = layers[pair.first].points[pair.second].position;
|
Vec3f current_pos = layers[pair.first].points[pair.second].position;
|
||||||
Vec2f fitted_pos = curve.get_fitted_value(current_pos.z());
|
Vec2f fitted_pos = curve.get_fitted_value(current_pos.z());
|
||||||
|
|
||||||
@ -1483,7 +1497,7 @@ void SeamPlacer::init(const Print &print, std::function<void(void)> throw_if_can
|
|||||||
for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) {
|
for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) {
|
||||||
std::vector<SeamCandidate> &layer_perimeter_points = layers[layer_idx].points;
|
std::vector<SeamCandidate> &layer_perimeter_points = layers[layer_idx].points;
|
||||||
for (size_t current = 0; current < layer_perimeter_points.size();
|
for (size_t current = 0; current < layer_perimeter_points.size();
|
||||||
current = layer_perimeter_points[current].perimeter.end_index + 1)
|
current = layer_perimeter_points[current].perimeter.end_index)
|
||||||
if (configured_seam_preference == spRandom)
|
if (configured_seam_preference == spRandom)
|
||||||
pick_random_seam_point(layer_perimeter_points, current);
|
pick_random_seam_point(layer_perimeter_points, current);
|
||||||
else
|
else
|
||||||
@ -1557,12 +1571,12 @@ void SeamPlacer::place_seam(const Layer *layer, ExtrusionLoop &loop, bool extern
|
|||||||
// the internal seam into the concave corner, and not on the perpendicular projection on the closest edge (which is what the split_at function does)
|
// the internal seam into the concave corner, and not on the perpendicular projection on the closest edge (which is what the split_at function does)
|
||||||
size_t index_of_prev =
|
size_t index_of_prev =
|
||||||
seam_index == perimeter_point.perimeter.start_index ?
|
seam_index == perimeter_point.perimeter.start_index ?
|
||||||
perimeter_point.perimeter.end_index :
|
perimeter_point.perimeter.end_index - 1 :
|
||||||
seam_index - 1;
|
seam_index - 1;
|
||||||
size_t index_of_next =
|
size_t index_of_next =
|
||||||
seam_index == perimeter_point.perimeter.end_index ?
|
seam_index == perimeter_point.perimeter.end_index - 1 ?
|
||||||
perimeter_point.perimeter.start_index :
|
perimeter_point.perimeter.start_index :
|
||||||
seam_index + 1;
|
seam_index + 1;
|
||||||
|
|
||||||
Vec2f dir_to_middle =
|
Vec2f dir_to_middle =
|
||||||
((perimeter_point.position - layer_perimeters.points[index_of_prev].position).head<2>().normalized()
|
((perimeter_point.position - layer_perimeters.points[index_of_prev].position).head<2>().normalized()
|
||||||
|
@ -39,16 +39,16 @@ enum class EnforcedBlockedSeamPoint {
|
|||||||
|
|
||||||
// struct representing single perimeter loop
|
// struct representing single perimeter loop
|
||||||
struct Perimeter {
|
struct Perimeter {
|
||||||
size_t start_index;
|
size_t start_index{};
|
||||||
size_t end_index; //inclusive!
|
size_t end_index{}; //inclusive!
|
||||||
size_t seam_index;
|
size_t seam_index{};
|
||||||
float flow_width;
|
float flow_width{};
|
||||||
|
|
||||||
// During alignment, a final position may be stored here. In that case, finalized is set to true.
|
// During alignment, a final position may be stored here. In that case, finalized is set to true.
|
||||||
// Note that final seam position is not limited to points of the perimeter loop. In theory it can be any position
|
// Note that final seam position is not limited to points of the perimeter loop. In theory it can be any position
|
||||||
// Random position also uses this flexibility to set final seam point position
|
// Random position also uses this flexibility to set final seam point position
|
||||||
bool finalized = false;
|
bool finalized = false;
|
||||||
Vec3f final_seam_position;
|
Vec3f final_seam_position = Vec3f::Zero();
|
||||||
};
|
};
|
||||||
|
|
||||||
//Struct over which all processing of perimeters is done. For each perimeter point, its respective candidate is created,
|
//Struct over which all processing of perimeters is done. For each perimeter point, its respective candidate is created,
|
||||||
@ -115,11 +115,10 @@ public:
|
|||||||
|
|
||||||
// arm length used during angles computation
|
// arm length used during angles computation
|
||||||
static constexpr float polygon_local_angles_arm_distance = 0.3f;
|
static constexpr float polygon_local_angles_arm_distance = 0.3f;
|
||||||
// value for angles with penalty lower than this threshold - such angles will be snapped to their original position instead of spline interpolated position
|
// snapping angle - angles larger than this value will be snapped to during seam painting
|
||||||
static constexpr float sharp_angle_penalty_snapping_threshold = 0.6f;
|
static constexpr float sharp_angle_snapping_threshold = 55.0f * float(PI) / 180.0f;
|
||||||
|
// overhang angle for seam placement that still yields good results, in degrees, measured from vertical direction
|
||||||
// max tolerable distance from the previous layer is overhang_distance_tolerance_factor * flow_width
|
static constexpr float overhang_angle_threshold = 45.0f * float(PI) / 180.0f;
|
||||||
static constexpr float overhang_distance_tolerance_factor = 0.5f;
|
|
||||||
|
|
||||||
// determines angle importance compared to visibility ( neutral value is 1.0f. )
|
// determines angle importance compared to visibility ( neutral value is 1.0f. )
|
||||||
static constexpr float angle_importance_aligned = 0.6f;
|
static constexpr float angle_importance_aligned = 0.6f;
|
||||||
@ -131,8 +130,8 @@ public:
|
|||||||
// When searching for seam clusters for alignment:
|
// When searching for seam clusters for alignment:
|
||||||
// following value describes, how much worse score can point have and still be picked into seam cluster instead of original seam point on the same layer
|
// following value describes, how much worse score can point have and still be picked into seam cluster instead of original seam point on the same layer
|
||||||
static constexpr float seam_align_score_tolerance = 0.3f;
|
static constexpr float seam_align_score_tolerance = 0.3f;
|
||||||
// seam_align_tolerable_dist - if next layer closest point is too far away, break aligned string
|
// seam_align_tolerable_dist_factor - how far to search for seam from current position, final dist is seam_align_tolerable_dist_factor * flow_width
|
||||||
static constexpr float seam_align_tolerable_dist = 1.0f;
|
static constexpr float seam_align_tolerable_dist_factor = 4.0f;
|
||||||
// minimum number of seams needed in cluster to make alignment happen
|
// minimum number of seams needed in cluster to make alignment happen
|
||||||
static constexpr size_t seam_align_minimum_string_seams = 6;
|
static constexpr size_t seam_align_minimum_string_seams = 6;
|
||||||
// millimeters covered by spline; determines number of splines for the given string
|
// millimeters covered by spline; determines number of splines for the given string
|
||||||
@ -154,8 +153,7 @@ private:
|
|||||||
void align_seam_points(const PrintObject *po, const SeamPlacerImpl::SeamComparator &comparator);
|
void align_seam_points(const PrintObject *po, const SeamPlacerImpl::SeamComparator &comparator);
|
||||||
std::vector<std::pair<size_t, size_t>> find_seam_string(const PrintObject *po,
|
std::vector<std::pair<size_t, size_t>> find_seam_string(const PrintObject *po,
|
||||||
std::pair<size_t, size_t> start_seam,
|
std::pair<size_t, size_t> start_seam,
|
||||||
const SeamPlacerImpl::SeamComparator &comparator,
|
const SeamPlacerImpl::SeamComparator &comparator) const;
|
||||||
float& string_weight) const;
|
|
||||||
std::optional<std::pair<size_t, size_t>> find_next_seam_in_layer(
|
std::optional<std::pair<size_t, size_t>> find_next_seam_in_layer(
|
||||||
const std::vector<PrintObjectSeamData::LayerSeams> &layers,
|
const std::vector<PrintObjectSeamData::LayerSeams> &layers,
|
||||||
const Vec3f& projected_position,
|
const Vec3f& projected_position,
|
||||||
|
@ -175,6 +175,19 @@ PiecewiseFittedCurve<Dimension, NumberType, Kernel> fit_curve(
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<int Dimension, typename NumberType>
|
||||||
|
PiecewiseFittedCurve<Dimension, NumberType, LinearKernel<NumberType>>
|
||||||
|
fit_linear_spline(
|
||||||
|
const std::vector<Vec<Dimension, NumberType>> &observations,
|
||||||
|
std::vector<NumberType> observation_points,
|
||||||
|
std::vector<NumberType> weights,
|
||||||
|
size_t segments_count,
|
||||||
|
size_t endpoints_level_of_freedom = 0) {
|
||||||
|
return fit_curve<LinearKernel<NumberType>>(observations, observation_points, weights, segments_count,
|
||||||
|
endpoints_level_of_freedom);
|
||||||
|
}
|
||||||
|
|
||||||
template<int Dimension, typename NumberType>
|
template<int Dimension, typename NumberType>
|
||||||
PiecewiseFittedCurve<Dimension, NumberType, CubicBSplineKernel<NumberType>>
|
PiecewiseFittedCurve<Dimension, NumberType, CubicBSplineKernel<NumberType>>
|
||||||
fit_cubic_bspline(
|
fit_cubic_bspline(
|
||||||
|
100
src/libslic3r/Geometry/VoronoiUtilsCgal.cpp
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
|
||||||
|
#include <CGAL/Arr_segment_traits_2.h>
|
||||||
|
#include <CGAL/Surface_sweep_2_algorithms.h>
|
||||||
|
|
||||||
|
#include "libslic3r/Geometry/Voronoi.hpp"
|
||||||
|
|
||||||
|
#include "VoronoiUtilsCgal.hpp"
|
||||||
|
|
||||||
|
using VD = Slic3r::Geometry::VoronoiDiagram;
|
||||||
|
|
||||||
|
namespace Slic3r::Geometry {
|
||||||
|
|
||||||
|
using CGAL_Point = CGAL::Exact_predicates_exact_constructions_kernel::Point_2;
|
||||||
|
using CGAL_Segment = CGAL::Arr_segment_traits_2<CGAL::Exact_predicates_exact_constructions_kernel>::Curve_2;
|
||||||
|
|
||||||
|
inline static CGAL_Point to_cgal_point(const VD::vertex_type &pt) { return {pt.x(), pt.y()}; }
|
||||||
|
|
||||||
|
// FIXME Lukas H.: Also includes parabolic segments.
|
||||||
|
bool VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(const VD &voronoi_diagram)
|
||||||
|
{
|
||||||
|
assert(std::all_of(voronoi_diagram.edges().cbegin(), voronoi_diagram.edges().cend(),
|
||||||
|
[](const VD::edge_type &edge) { return edge.color() == 0; }));
|
||||||
|
|
||||||
|
std::vector<CGAL_Segment> segments;
|
||||||
|
segments.reserve(voronoi_diagram.num_edges());
|
||||||
|
|
||||||
|
for (const VD::edge_type &edge : voronoi_diagram.edges()) {
|
||||||
|
if (edge.color() != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (edge.is_finite() && edge.is_linear()) {
|
||||||
|
segments.emplace_back(to_cgal_point(*edge.vertex0()), to_cgal_point(*edge.vertex1()));
|
||||||
|
edge.color(1);
|
||||||
|
assert(edge.twin() != nullptr);
|
||||||
|
edge.twin()->color(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const VD::edge_type &edge : voronoi_diagram.edges())
|
||||||
|
edge.color(0);
|
||||||
|
|
||||||
|
std::vector<CGAL_Point> intersections_pt;
|
||||||
|
CGAL::compute_intersection_points(segments.begin(), segments.end(), std::back_inserter(intersections_pt));
|
||||||
|
return intersections_pt.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool check_if_three_vectors_are_ccw(const CGAL_Point &common_pt, const CGAL_Point &pt_1, const CGAL_Point &pt_2, const CGAL_Point &test_pt) {
|
||||||
|
CGAL::Orientation orientation = CGAL::orientation(common_pt, pt_1, pt_2);
|
||||||
|
if (orientation == CGAL::Orientation::COLLINEAR) {
|
||||||
|
// The first two edges are collinear, so the third edge must be on the right side on the first of them.
|
||||||
|
return CGAL::orientation(common_pt, pt_1, test_pt) == CGAL::Orientation::RIGHT_TURN;
|
||||||
|
} else if (orientation == CGAL::Orientation::LEFT_TURN) {
|
||||||
|
// CCW oriented angle between vectors (common_pt, pt1) and (common_pt, pt2) is bellow PI.
|
||||||
|
// So we need to check if test_pt isn't between them.
|
||||||
|
CGAL::Orientation orientation1 = CGAL::orientation(common_pt, pt_1, test_pt);
|
||||||
|
CGAL::Orientation orientation2 = CGAL::orientation(common_pt, pt_2, test_pt);
|
||||||
|
return (orientation1 != CGAL::Orientation::LEFT_TURN || orientation2 != CGAL::Orientation::RIGHT_TURN);
|
||||||
|
} else {
|
||||||
|
assert(orientation == CGAL::Orientation::RIGHT_TURN);
|
||||||
|
// CCW oriented angle between vectors (common_pt, pt1) and (common_pt, pt2) is upper PI.
|
||||||
|
// So we need to check if test_pt is between them.
|
||||||
|
CGAL::Orientation orientation1 = CGAL::orientation(common_pt, pt_1, test_pt);
|
||||||
|
CGAL::Orientation orientation2 = CGAL::orientation(common_pt, pt_2, test_pt);
|
||||||
|
return (orientation1 == CGAL::Orientation::RIGHT_TURN || orientation2 == CGAL::Orientation::LEFT_TURN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(const VoronoiDiagram &voronoi_diagram)
|
||||||
|
{
|
||||||
|
for (const VD::vertex_type &vertex : voronoi_diagram.vertices()) {
|
||||||
|
std::vector<const VD::edge_type *> edges;
|
||||||
|
const VD::edge_type *edge = vertex.incident_edge();
|
||||||
|
|
||||||
|
do {
|
||||||
|
// FIXME Lukas H.: Also process parabolic segments.
|
||||||
|
if (edge->is_finite() && edge->is_linear())
|
||||||
|
edges.emplace_back(edge);
|
||||||
|
|
||||||
|
edge = edge->rot_next();
|
||||||
|
} while (edge != vertex.incident_edge());
|
||||||
|
|
||||||
|
// Checking for CCW make sense for three and more edges.
|
||||||
|
if (edges.size() > 2) {
|
||||||
|
for (auto edge_it = edges.begin() ; edge_it != edges.end(); ++edge_it) {
|
||||||
|
const Geometry::VoronoiDiagram::edge_type *prev_edge = edge_it == edges.begin() ? edges.back() : *std::prev(edge_it);
|
||||||
|
const Geometry::VoronoiDiagram::edge_type *curr_edge = *edge_it;
|
||||||
|
const Geometry::VoronoiDiagram::edge_type *next_edge = std::next(edge_it) == edges.end() ? edges.front() : *std::next(edge_it);
|
||||||
|
|
||||||
|
if (!check_if_three_vectors_are_ccw(to_cgal_point(*prev_edge->vertex0()), to_cgal_point(*prev_edge->vertex1()),
|
||||||
|
to_cgal_point(*curr_edge->vertex1()), to_cgal_point(*next_edge->vertex1())))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace Slic3r::Geometry
|
21
src/libslic3r/Geometry/VoronoiUtilsCgal.hpp
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#ifndef slic3r_VoronoiUtilsCgal_hpp_
|
||||||
|
#define slic3r_VoronoiUtilsCgal_hpp_
|
||||||
|
|
||||||
|
#include "Voronoi.hpp"
|
||||||
|
|
||||||
|
namespace Slic3r::Geometry {
|
||||||
|
class VoronoiDiagram;
|
||||||
|
|
||||||
|
class VoronoiUtilsCgal
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Check if the Voronoi diagram is planar using CGAL sweeping edge algorithm for enumerating all intersections between lines.
|
||||||
|
static bool is_voronoi_diagram_planar_intersection(const VoronoiDiagram &voronoi_diagram);
|
||||||
|
|
||||||
|
// Check if the Voronoi diagram is planar using verification that all neighboring edges are ordered CCW for each vertex.
|
||||||
|
static bool is_voronoi_diagram_planar_angle(const VoronoiDiagram &voronoi_diagram);
|
||||||
|
|
||||||
|
};
|
||||||
|
} // namespace Slic3r::Geometry
|
||||||
|
|
||||||
|
#endif // slic3r_VoronoiUtilsCgal_hpp_
|
@ -13,6 +13,7 @@
|
|||||||
#include "Format/OBJ.hpp"
|
#include "Format/OBJ.hpp"
|
||||||
#include "Format/STL.hpp"
|
#include "Format/STL.hpp"
|
||||||
#include "Format/3mf.hpp"
|
#include "Format/3mf.hpp"
|
||||||
|
#include "Format/STEP.hpp"
|
||||||
|
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
|
|
||||||
@ -114,13 +115,15 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
|
|||||||
result = load_stl(input_file.c_str(), &model);
|
result = load_stl(input_file.c_str(), &model);
|
||||||
else if (boost::algorithm::iends_with(input_file, ".obj"))
|
else if (boost::algorithm::iends_with(input_file, ".obj"))
|
||||||
result = load_obj(input_file.c_str(), &model);
|
result = load_obj(input_file.c_str(), &model);
|
||||||
|
else if (boost::algorithm::iends_with(input_file, ".step") || boost::algorithm::iends_with(input_file, ".stp"))
|
||||||
|
result = load_step(input_file.c_str(), &model);
|
||||||
else if (boost::algorithm::iends_with(input_file, ".amf") || boost::algorithm::iends_with(input_file, ".amf.xml"))
|
else if (boost::algorithm::iends_with(input_file, ".amf") || boost::algorithm::iends_with(input_file, ".amf.xml"))
|
||||||
result = load_amf(input_file.c_str(), config, config_substitutions, &model, options & LoadAttribute::CheckVersion);
|
result = load_amf(input_file.c_str(), config, config_substitutions, &model, options & LoadAttribute::CheckVersion);
|
||||||
else if (boost::algorithm::iends_with(input_file, ".3mf"))
|
else if (boost::algorithm::iends_with(input_file, ".3mf"))
|
||||||
//FIXME options & LoadAttribute::CheckVersion ?
|
//FIXME options & LoadAttribute::CheckVersion ?
|
||||||
result = load_3mf(input_file.c_str(), *config, *config_substitutions, &model, false);
|
result = load_3mf(input_file.c_str(), *config, *config_substitutions, &model, false);
|
||||||
else
|
else
|
||||||
throw Slic3r::RuntimeError("Unknown file format. Input file must have .stl, .obj, .amf(.xml) or .prusa extension.");
|
throw Slic3r::RuntimeError("Unknown file format. Input file must have .stl, .obj, .amf(.xml), .prusa or .step/.stp extension.");
|
||||||
|
|
||||||
if (! result)
|
if (! result)
|
||||||
throw Slic3r::RuntimeError("Loading of a model file failed.");
|
throw Slic3r::RuntimeError("Loading of a model file failed.");
|
||||||
|
@ -10,6 +10,14 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
//#define ARACHNE_DEBUG
|
||||||
|
|
||||||
|
#ifdef ARACHNE_DEBUG
|
||||||
|
#include "SVG.hpp"
|
||||||
|
#include "Utils.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
@ -24,7 +32,18 @@ ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thick_poly
|
|||||||
assert(line.a_width >= SCALED_EPSILON && line.b_width >= SCALED_EPSILON);
|
assert(line.a_width >= SCALED_EPSILON && line.b_width >= SCALED_EPSILON);
|
||||||
|
|
||||||
const coordf_t line_len = line.length();
|
const coordf_t line_len = line.length();
|
||||||
if (line_len < SCALED_EPSILON) continue;
|
if (line_len < SCALED_EPSILON) {
|
||||||
|
// The line is so tiny that we don't care about its width when we connect it to another line.
|
||||||
|
if (!path.empty())
|
||||||
|
path.polyline.points.back() = line.b; // If the variable path is non-empty, connect this tiny line to it.
|
||||||
|
else if (i + 1 < (int)lines.size()) // If there is at least one following line, connect this tiny line to it.
|
||||||
|
lines[i + 1].a = line.a;
|
||||||
|
else if (!paths.empty())
|
||||||
|
paths.back().polyline.points.back() = line.b; // Connect this tiny line to the last finished path.
|
||||||
|
|
||||||
|
// If any of the above isn't satisfied, then remove this tiny line.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
double thickness_delta = fabs(line.a_width - line.b_width);
|
double thickness_delta = fabs(line.a_width - line.b_width);
|
||||||
if (thickness_delta > tolerance) {
|
if (thickness_delta > tolerance) {
|
||||||
@ -100,14 +119,19 @@ static void variable_width(const ThickPolylines& polylines, ExtrusionRole role,
|
|||||||
// This value determines granularity of adaptive width, as G-code does not allow
|
// This value determines granularity of adaptive width, as G-code does not allow
|
||||||
// variable extrusion within a single move; this value shall only affect the amount
|
// variable extrusion within a single move; this value shall only affect the amount
|
||||||
// of segments, and any pruning shall be performed before we apply this tolerance.
|
// of segments, and any pruning shall be performed before we apply this tolerance.
|
||||||
const float tolerance = float(scale_(0.05));
|
const auto tolerance = float(scale_(0.05));
|
||||||
for (const ThickPolyline &p : polylines) {
|
for (const ThickPolyline &p : polylines) {
|
||||||
ExtrusionPaths paths = thick_polyline_to_extrusion_paths(p, role, flow, tolerance, tolerance);
|
ExtrusionPaths paths = thick_polyline_to_extrusion_paths(p, role, flow, tolerance, tolerance);
|
||||||
// Append paths to collection.
|
// Append paths to collection.
|
||||||
if (! paths.empty()) {
|
if (!paths.empty()) {
|
||||||
if (paths.front().first_point() == paths.back().last_point())
|
for (auto it = std::next(paths.begin()); it != paths.end(); ++it) {
|
||||||
out.emplace_back(new ExtrusionLoop(std::move(paths)));
|
assert(it->polyline.points.size() >= 2);
|
||||||
else {
|
assert(std::prev(it)->polyline.last_point() == it->polyline.first_point());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (paths.front().first_point() == paths.back().last_point()) {
|
||||||
|
out.emplace_back(new ExtrusionLoop(std::move(paths)));
|
||||||
|
} else {
|
||||||
for (ExtrusionPath &path : paths)
|
for (ExtrusionPath &path : paths)
|
||||||
out.emplace_back(new ExtrusionPath(std::move(path)));
|
out.emplace_back(new ExtrusionPath(std::move(path)));
|
||||||
}
|
}
|
||||||
@ -462,8 +486,40 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator &p
|
|||||||
// Reapply the nearest point search for starting point.
|
// Reapply the nearest point search for starting point.
|
||||||
// We allow polyline reversal because Clipper may have randomly reversed polylines during clipping.
|
// We allow polyline reversal because Clipper may have randomly reversed polylines during clipping.
|
||||||
// Arachne sometimes creates extrusion with zero-length (just two same endpoints);
|
// Arachne sometimes creates extrusion with zero-length (just two same endpoints);
|
||||||
if (!paths.empty())
|
if (!paths.empty()) {
|
||||||
chain_and_reorder_extrusion_paths(paths, &paths.front().first_point());
|
Point start_point = paths.front().first_point();
|
||||||
|
if (!extrusion->is_closed) {
|
||||||
|
// Especially for open extrusion, we need to select a starting point that is at the start
|
||||||
|
// or the end of the extrusions to make one continuous line. Also, we prefer a non-overhang
|
||||||
|
// starting point.
|
||||||
|
struct PointInfo
|
||||||
|
{
|
||||||
|
size_t occurrence = 0;
|
||||||
|
bool is_overhang = false;
|
||||||
|
};
|
||||||
|
std::unordered_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;
|
||||||
|
if (path.role() == erOverhangPerimeter) {
|
||||||
|
point_occurrence[path.polyline.first_point()].is_overhang = true;
|
||||||
|
point_occurrence[path.polyline.last_point()].is_overhang = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefer non-overhang point as a starting point.
|
||||||
|
for (const std::pair<Point, PointInfo> pt : point_occurrence)
|
||||||
|
if (pt.second.occurrence == 1) {
|
||||||
|
start_point = pt.first;
|
||||||
|
if (!pt.second.is_overhang) {
|
||||||
|
start_point = pt.first;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chain_and_reorder_extrusion_paths(paths, &start_point);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
extrusion_paths_append(paths, *extrusion, role, is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow);
|
extrusion_paths_append(paths, *extrusion, role, is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow);
|
||||||
}
|
}
|
||||||
@ -488,6 +544,27 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator &p
|
|||||||
return extrusion_coll;
|
return extrusion_coll;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ARACHNE_DEBUG
|
||||||
|
static void export_perimeters_to_svg(const std::string &path, const Polygons &contours, const std::vector<Arachne::VariableWidthLines> &perimeters, const ExPolygons &infill_area)
|
||||||
|
{
|
||||||
|
coordf_t stroke_width = scale_(0.03);
|
||||||
|
BoundingBox bbox = get_extents(contours);
|
||||||
|
bbox.offset(scale_(1.));
|
||||||
|
::Slic3r::SVG svg(path.c_str(), bbox);
|
||||||
|
|
||||||
|
svg.draw(infill_area, "cyan");
|
||||||
|
|
||||||
|
for (const Arachne::VariableWidthLines &perimeter : perimeters)
|
||||||
|
for (const Arachne::ExtrusionLine &extrusion_line : perimeter) {
|
||||||
|
ThickPolyline thick_polyline = to_thick_polyline(extrusion_line);
|
||||||
|
svg.draw({thick_polyline}, "green", "blue", stroke_width);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const Line &line : to_lines(contours))
|
||||||
|
svg.draw(line, "red", stroke_width);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Thanks, Cura developers, for implementing an algorithm for generating perimeters with variable width (Arachne) that is based on the paper
|
// Thanks, Cura developers, for implementing an algorithm for generating perimeters with variable width (Arachne) that is based on the paper
|
||||||
// "A framework for adaptive width control of dense contour-parallel toolpaths in fused deposition modeling"
|
// "A framework for adaptive width control of dense contour-parallel toolpaths in fused deposition modeling"
|
||||||
void PerimeterGenerator::process_arachne()
|
void PerimeterGenerator::process_arachne()
|
||||||
@ -525,10 +602,28 @@ void PerimeterGenerator::process_arachne()
|
|||||||
ExPolygons last = offset_ex(surface.expolygon.simplify_p(m_scaled_resolution), - float(ext_perimeter_width / 2. - ext_perimeter_spacing / 2.));
|
ExPolygons last = offset_ex(surface.expolygon.simplify_p(m_scaled_resolution), - float(ext_perimeter_width / 2. - ext_perimeter_spacing / 2.));
|
||||||
Polygons last_p = to_polygons(last);
|
Polygons last_p = to_polygons(last);
|
||||||
|
|
||||||
Arachne::WallToolPaths wallToolPaths(last_p, ext_perimeter_spacing, perimeter_spacing, coord_t(loop_number + 1), 0, *this->object_config, *this->print_config);
|
Arachne::WallToolPaths wallToolPaths(last_p, ext_perimeter_spacing, perimeter_spacing, coord_t(loop_number + 1), 0, layer_height, *this->object_config, *this->print_config);
|
||||||
std::vector<Arachne::VariableWidthLines> perimeters = wallToolPaths.getToolPaths();
|
std::vector<Arachne::VariableWidthLines> perimeters = wallToolPaths.getToolPaths();
|
||||||
loop_number = int(perimeters.size()) - 1;
|
loop_number = int(perimeters.size()) - 1;
|
||||||
|
|
||||||
|
#ifdef ARACHNE_DEBUG
|
||||||
|
{
|
||||||
|
static int iRun = 0;
|
||||||
|
export_perimeters_to_svg(debug_out_path("arachne-perimeters-%d-%d.svg", layer_id, iRun++), to_polygons(last), perimeters, union_ex(wallToolPaths.getInnerContour()));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// All closed ExtrusionLine should have the same the first and the last point.
|
||||||
|
// But in rare cases, Arachne produce ExtrusionLine marked as closed but without
|
||||||
|
// equal the first and the last point.
|
||||||
|
assert([&perimeters = std::as_const(perimeters)]() -> bool {
|
||||||
|
for (const Arachne::VariableWidthLines &perimeter : perimeters)
|
||||||
|
for (const Arachne::ExtrusionLine &el : perimeter)
|
||||||
|
if (el.is_closed && el.junctions.front().p != el.junctions.back().p)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}());
|
||||||
|
|
||||||
int start_perimeter = int(perimeters.size()) - 1;
|
int start_perimeter = int(perimeters.size()) - 1;
|
||||||
int end_perimeter = -1;
|
int end_perimeter = -1;
|
||||||
int direction = -1;
|
int direction = -1;
|
||||||
|
@ -448,7 +448,7 @@ static std::vector<std::string> s_Preset_print_options {
|
|||||||
"wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width",
|
"wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width",
|
||||||
"wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits",
|
"wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits",
|
||||||
"perimeter_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle",
|
"perimeter_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle",
|
||||||
"wall_distribution_count", "wall_split_middle_threshold", "wall_add_middle_threshold", "min_feature_size", "min_bead_width"
|
"wall_distribution_count", "min_feature_size", "min_bead_width"
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::vector<std::string> s_Preset_filament_options {
|
static std::vector<std::string> s_Preset_filament_options {
|
||||||
|
@ -124,7 +124,7 @@ public:
|
|||||||
T * const * begin() const { return m_data->data(); }
|
T * const * begin() const { return m_data->data(); }
|
||||||
T * const * end() const { return m_data->data() + m_data->size(); }
|
T * const * end() const { return m_data->data() + m_data->size(); }
|
||||||
const T* front() const { return m_data->front(); }
|
const T* front() const { return m_data->front(); }
|
||||||
const T* back() const { return m_data->front(); }
|
const T* back() const { return m_data->back(); }
|
||||||
size_t size() const { return m_data->size(); }
|
size_t size() const { return m_data->size(); }
|
||||||
bool empty() const { return m_data->empty(); }
|
bool empty() const { return m_data->empty(); }
|
||||||
const T* operator[](size_t i) const { return (*m_data)[i]; }
|
const T* operator[](size_t i) const { return (*m_data)[i]; }
|
||||||
|
@ -3075,7 +3075,8 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->category = L("Layers and Perimeters");
|
def->category = L("Layers and Perimeters");
|
||||||
def->tooltip = L("Classic perimeter generator produces perimeters with constant extrusion width and for "
|
def->tooltip = L("Classic perimeter generator produces perimeters with constant extrusion width and for "
|
||||||
"very thin areas is used gap-fill. "
|
"very thin areas is used gap-fill. "
|
||||||
"Arachne engine produces perimeters with variable extrusion width.");
|
"Arachne engine produces perimeters with variable extrusion width. "
|
||||||
|
"This setting also affects the Concentric infill.");
|
||||||
def->enum_keys_map = &ConfigOptionEnum<PerimeterGeneratorType>::get_enum_values();
|
def->enum_keys_map = &ConfigOptionEnum<PerimeterGeneratorType>::get_enum_values();
|
||||||
def->enum_values.push_back("classic");
|
def->enum_values.push_back("classic");
|
||||||
def->enum_values.push_back("arachne");
|
def->enum_values.push_back("arachne");
|
||||||
@ -3084,15 +3085,16 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->mode = comAdvanced;
|
def->mode = comAdvanced;
|
||||||
def->set_default_value(new ConfigOptionEnum<PerimeterGeneratorType>(PerimeterGeneratorType::Arachne));
|
def->set_default_value(new ConfigOptionEnum<PerimeterGeneratorType>(PerimeterGeneratorType::Arachne));
|
||||||
|
|
||||||
def = this->add("wall_transition_length", coFloat);
|
def = this->add("wall_transition_length", coFloatOrPercent);
|
||||||
def->label = L("Perimeter transition length");
|
def->label = L("Perimeter transition length");
|
||||||
def->category = L("Advanced");
|
def->category = L("Advanced");
|
||||||
def->tooltip = L("When transitioning between different numbers of perimeters as the part becomes "
|
def->tooltip = L("When transitioning between different numbers of perimeters as the part becomes "
|
||||||
"thinner, a certain amount of space is allotted to split or join the perimeter segments.");
|
"thinner, a certain amount of space is allotted to split or join the perimeter segments. "
|
||||||
def->sidetext = L("mm");
|
"If expressed as a percentage (for example 100%), it will be computed based on the nozzle diameter.");
|
||||||
|
def->sidetext = L("mm or %");
|
||||||
def->mode = comExpert;
|
def->mode = comExpert;
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
def->set_default_value(new ConfigOptionFloat(0.4));
|
def->set_default_value(new ConfigOptionFloatOrPercent(100, true));
|
||||||
|
|
||||||
def = this->add("wall_transition_filter_deviation", coFloatOrPercent);
|
def = this->add("wall_transition_filter_deviation", coFloatOrPercent);
|
||||||
def->label = L("Perimeter transitioning filter margin");
|
def->label = L("Perimeter transitioning filter margin");
|
||||||
@ -3131,46 +3133,17 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->min = 1;
|
def->min = 1;
|
||||||
def->set_default_value(new ConfigOptionInt(1));
|
def->set_default_value(new ConfigOptionInt(1));
|
||||||
|
|
||||||
def = this->add("wall_split_middle_threshold", coPercent);
|
def = this->add("min_feature_size", coFloatOrPercent);
|
||||||
def->label = L("Split middle perimeter threshold");
|
|
||||||
def->category = L("Advanced");
|
|
||||||
def->tooltip = L("The smallest extrusion width, as a factor of the normal extrusion width, above which the middle "
|
|
||||||
"perimeter (if there is one) will be split into two. Reduce this setting to use more, thinner "
|
|
||||||
"perimeters. Increase to use fewer, wider perimeters. Note that this applies -as if- the entire "
|
|
||||||
"shape should be filled with perimeter, so the middle here refers to the middle of the object "
|
|
||||||
"between two outer edges of the shape, even if there actually is infill or other extrusion types in "
|
|
||||||
"the print instead of the perimeter.");
|
|
||||||
def->sidetext = L("%");
|
|
||||||
def->mode = comAdvanced;
|
|
||||||
def->min = 1;
|
|
||||||
def->max = 99;
|
|
||||||
def->set_default_value(new ConfigOptionPercent(50));
|
|
||||||
|
|
||||||
def = this->add("wall_add_middle_threshold", coPercent);
|
|
||||||
def->label = L("Add middle perimeter threshold");
|
|
||||||
def->category = L("Advanced");
|
|
||||||
def->tooltip = L("The smallest extrusion width, as a factor of the normal extrusion width, above which a middle "
|
|
||||||
"perimeter (if there wasn't one already) will be added. Reduce this setting to use more, "
|
|
||||||
"thinner perimeters. Increase to use fewer, wider perimeters. Note that this applies -as if- the "
|
|
||||||
"entire shape should be filled with perimeter, so the middle here refers to the middle of the "
|
|
||||||
"object between two outer edges of the shape, even if there actually is infill or other "
|
|
||||||
"extrusion types in the print instead of the perimeter.");
|
|
||||||
def->sidetext = L("%");
|
|
||||||
def->mode = comAdvanced;
|
|
||||||
def->min = 1;
|
|
||||||
def->max = 99;
|
|
||||||
def->set_default_value(new ConfigOptionPercent(75));
|
|
||||||
|
|
||||||
def = this->add("min_feature_size", coFloat);
|
|
||||||
def->label = L("Minimum feature size");
|
def->label = L("Minimum feature size");
|
||||||
def->category = L("Advanced");
|
def->category = L("Advanced");
|
||||||
def->tooltip = L("Minimum thickness of thin features. Model features that are thinner than this value will "
|
def->tooltip = L("Minimum thickness of thin features. Model features that are thinner than this value will "
|
||||||
"not be printed, while features thicker than the Minimum feature size will be widened to "
|
"not be printed, while features thicker than the Minimum feature size will be widened to "
|
||||||
"the Minimum perimeter width.");
|
"the Minimum perimeter width. "
|
||||||
def->sidetext = L("mm");
|
"If expressed as a percentage (for example 25%), it will be computed based on the nozzle diameter.");
|
||||||
|
def->sidetext = L("mm or %");
|
||||||
def->mode = comExpert;
|
def->mode = comExpert;
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
def->set_default_value(new ConfigOptionFloat(0.1));
|
def->set_default_value(new ConfigOptionFloatOrPercent(25, true));
|
||||||
|
|
||||||
def = this->add("min_bead_width", coFloatOrPercent);
|
def = this->add("min_bead_width", coFloatOrPercent);
|
||||||
def->label = L("Minimum perimeter width");
|
def->label = L("Minimum perimeter width");
|
||||||
@ -4048,6 +4021,8 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
|||||||
"serial_port", "serial_speed",
|
"serial_port", "serial_speed",
|
||||||
// Introduced in some PrusaSlicer 2.3.1 alpha, later renamed or removed.
|
// Introduced in some PrusaSlicer 2.3.1 alpha, later renamed or removed.
|
||||||
"fuzzy_skin_perimeter_mode", "fuzzy_skin_shape",
|
"fuzzy_skin_perimeter_mode", "fuzzy_skin_shape",
|
||||||
|
// Introduced in PrusaSlicer 2.3.0-alpha2, later replaced by automatic calculation based on extrusion width.
|
||||||
|
"wall_add_middle_threshold", "wall_split_middle_threshold",
|
||||||
};
|
};
|
||||||
|
|
||||||
// In PrusaSlicer 2.3.0-alpha0 the "monotonous" infill was introduced, which was later renamed to "monotonic".
|
// In PrusaSlicer 2.3.0-alpha0 the "monotonous" infill was introduced, which was later renamed to "monotonic".
|
||||||
|
@ -508,13 +508,11 @@ PRINT_CONFIG_CLASS_DEFINE(
|
|||||||
((ConfigOptionFloat, slice_closing_radius))
|
((ConfigOptionFloat, slice_closing_radius))
|
||||||
((ConfigOptionEnum<SlicingMode>, slicing_mode))
|
((ConfigOptionEnum<SlicingMode>, slicing_mode))
|
||||||
((ConfigOptionEnum<PerimeterGeneratorType>, perimeter_generator))
|
((ConfigOptionEnum<PerimeterGeneratorType>, perimeter_generator))
|
||||||
((ConfigOptionFloat, wall_transition_length))
|
((ConfigOptionFloatOrPercent, wall_transition_length))
|
||||||
((ConfigOptionFloatOrPercent, wall_transition_filter_deviation))
|
((ConfigOptionFloatOrPercent, wall_transition_filter_deviation))
|
||||||
((ConfigOptionFloat, wall_transition_angle))
|
((ConfigOptionFloat, wall_transition_angle))
|
||||||
((ConfigOptionInt, wall_distribution_count))
|
((ConfigOptionInt, wall_distribution_count))
|
||||||
((ConfigOptionPercent, wall_split_middle_threshold))
|
((ConfigOptionFloatOrPercent, min_feature_size))
|
||||||
((ConfigOptionPercent, wall_add_middle_threshold))
|
|
||||||
((ConfigOptionFloat, min_feature_size))
|
|
||||||
((ConfigOptionFloatOrPercent, min_bead_width))
|
((ConfigOptionFloatOrPercent, min_bead_width))
|
||||||
((ConfigOptionBool, support_material))
|
((ConfigOptionBool, support_material))
|
||||||
// Automatic supports (generated based on support_material_threshold).
|
// Automatic supports (generated based on support_material_threshold).
|
||||||
|
@ -673,8 +673,6 @@ bool PrintObject::invalidate_state_by_config_options(
|
|||||||
|| opt_key == "wall_transition_filter_deviation"
|
|| opt_key == "wall_transition_filter_deviation"
|
||||||
|| opt_key == "wall_transition_angle"
|
|| opt_key == "wall_transition_angle"
|
||||||
|| opt_key == "wall_distribution_count"
|
|| opt_key == "wall_distribution_count"
|
||||||
|| opt_key == "wall_split_middle_threshold"
|
|
||||||
|| opt_key == "wall_add_middle_threshold"
|
|
||||||
|| opt_key == "min_feature_size"
|
|| opt_key == "min_feature_size"
|
||||||
|| opt_key == "min_bead_width") {
|
|| opt_key == "min_bead_width") {
|
||||||
steps.emplace_back(posSlice);
|
steps.emplace_back(posSlice);
|
||||||
|
@ -274,26 +274,29 @@ std::string SVG::get_path_d(const ClipperLib::Path &path, double scale, bool clo
|
|||||||
return d.str();
|
return d.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SVG::draw_text(const Point &pt, const char *text, const char *color)
|
void SVG::draw_text(const Point &pt, const char *text, const char *color, const coordf_t font_size)
|
||||||
{
|
{
|
||||||
fprintf(this->f,
|
fprintf(this->f,
|
||||||
"<text x=\"%f\" y=\"%f\" font-family=\"sans-serif\" font-size=\"20px\" fill=\"%s\">%s</text>",
|
R"(<text x="%f" y="%f" font-family="sans-serif" font-size="%fpx" fill="%s">%s</text>)",
|
||||||
to_svg_x(pt(0)-origin(0)),
|
to_svg_x(float(pt.x() - origin.x())),
|
||||||
to_svg_y(pt(1)-origin(1)),
|
to_svg_y(float(pt.y() - origin.y())),
|
||||||
|
font_size,
|
||||||
color, text);
|
color, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SVG::draw_legend(const Point &pt, const char *text, const char *color)
|
void SVG::draw_legend(const Point &pt, const char *text, const char *color, const coordf_t font_size)
|
||||||
{
|
{
|
||||||
fprintf(this->f,
|
fprintf(this->f,
|
||||||
"<circle cx=\"%f\" cy=\"%f\" r=\"10\" fill=\"%s\"/>",
|
R"(<circle cx="%f" cy="%f" r="%f" fill="%s"/>)",
|
||||||
to_svg_x(pt(0)-origin(0)),
|
to_svg_x(float(pt.x() - origin.x())),
|
||||||
to_svg_y(pt(1)-origin(1)),
|
to_svg_y(float(pt.y() - origin.y())),
|
||||||
|
font_size,
|
||||||
color);
|
color);
|
||||||
fprintf(this->f,
|
fprintf(this->f,
|
||||||
"<text x=\"%f\" y=\"%f\" font-family=\"sans-serif\" font-size=\"10px\" fill=\"%s\">%s</text>",
|
R"(<text x="%f" y="%f" font-family="sans-serif" font-size="%fpx" fill="%s">%s</text>)",
|
||||||
to_svg_x(pt(0)-origin(0)) + 20.f,
|
to_svg_x(float(pt.x() - origin.x())) + 20.f,
|
||||||
to_svg_y(pt(1)-origin(1)),
|
to_svg_y(float(pt.y() - origin.y())),
|
||||||
|
font_size,
|
||||||
"black", text);
|
"black", text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,8 +72,8 @@ public:
|
|||||||
void draw(const ClipperLib::Path &polygon, double scale, std::string fill = "grey", coordf_t stroke_width = 0);
|
void draw(const ClipperLib::Path &polygon, double scale, std::string fill = "grey", coordf_t stroke_width = 0);
|
||||||
void draw(const ClipperLib::Paths &polygons, double scale, std::string fill = "grey", coordf_t stroke_width = 0);
|
void draw(const ClipperLib::Paths &polygons, double scale, std::string fill = "grey", coordf_t stroke_width = 0);
|
||||||
|
|
||||||
void draw_text(const Point &pt, const char *text, const char *color);
|
void draw_text(const Point &pt, const char *text, const char *color, coordf_t font_size = 20.f);
|
||||||
void draw_legend(const Point &pt, const char *text, const char *color);
|
void draw_legend(const Point &pt, const char *text, const char *color, coordf_t font_size = 10.f);
|
||||||
|
|
||||||
void Close();
|
void Close();
|
||||||
|
|
||||||
|
@ -1440,4 +1440,5 @@ bool its_write_stl_binary(const char *file, const char *label, const std::vector
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
60
src/occt_wrapper/CMakeLists.txt
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
project(OCCTWrapper)
|
||||||
|
|
||||||
|
if (APPLE)
|
||||||
|
# TODO: we need to fix notarization with the separate shared library
|
||||||
|
add_library(OCCTWrapper STATIC OCCTWrapper.cpp)
|
||||||
|
else ()
|
||||||
|
add_library(OCCTWrapper MODULE OCCTWrapper.cpp)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set_target_properties(OCCTWrapper
|
||||||
|
PROPERTIES
|
||||||
|
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/src"
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/src"
|
||||||
|
PREFIX ""
|
||||||
|
)
|
||||||
|
|
||||||
|
include(GenerateExportHeader)
|
||||||
|
|
||||||
|
generate_export_header(OCCTWrapper)
|
||||||
|
|
||||||
|
find_package(OpenCASCADE 7.6.2 REQUIRED)
|
||||||
|
|
||||||
|
set(OCCT_LIBS
|
||||||
|
TKXDESTEP
|
||||||
|
TKSTEP
|
||||||
|
TKSTEP209
|
||||||
|
TKSTEPAttr
|
||||||
|
TKSTEPBase
|
||||||
|
TKXCAF
|
||||||
|
TKXSBase
|
||||||
|
TKVCAF
|
||||||
|
TKCAF
|
||||||
|
TKLCAF
|
||||||
|
TKCDF
|
||||||
|
TKV3d
|
||||||
|
TKService
|
||||||
|
TKMesh
|
||||||
|
TKBO
|
||||||
|
TKPrim
|
||||||
|
TKHLR
|
||||||
|
TKShHealing
|
||||||
|
TKTopAlgo
|
||||||
|
TKGeomAlgo
|
||||||
|
TKBRep
|
||||||
|
TKGeomBase
|
||||||
|
TKG3d
|
||||||
|
TKG2d
|
||||||
|
TKMath
|
||||||
|
TKernel
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(OCCTWrapper PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
target_include_directories(OCCTWrapper PUBLIC ${OpenCASCADE_INCLUDE_DIR})
|
||||||
|
target_link_libraries(OCCTWrapper ${OCCT_LIBS})
|
||||||
|
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
|
install(TARGETS OCCTWrapper DESTINATION "${CMAKE_INSTALL_BINDIR}")
|
||||||
|
|
201
src/occt_wrapper/OCCTWrapper.cpp
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
#include "OCCTWrapper.hpp"
|
||||||
|
|
||||||
|
#include "occtwrapper_export.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define DIR_SEPARATOR '\\'
|
||||||
|
#else
|
||||||
|
#define DIR_SEPARATOR '/'
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "STEPCAFControl_Reader.hxx"
|
||||||
|
#include "BRepMesh_IncrementalMesh.hxx"
|
||||||
|
#include "XCAFDoc_DocumentTool.hxx"
|
||||||
|
#include "XCAFDoc_ShapeTool.hxx"
|
||||||
|
#include "XCAFApp_Application.hxx"
|
||||||
|
#include "TopoDS_Builder.hxx"
|
||||||
|
#include "TopoDS.hxx"
|
||||||
|
#include "TDataStd_Name.hxx"
|
||||||
|
#include "BRepBuilderAPI_Transform.hxx"
|
||||||
|
#include "TopExp_Explorer.hxx"
|
||||||
|
#include "BRep_Tool.hxx"
|
||||||
|
|
||||||
|
const double STEP_TRANS_CHORD_ERROR = 0.005;
|
||||||
|
const double STEP_TRANS_ANGLE_RES = 1;
|
||||||
|
|
||||||
|
// const int LOAD_STEP_STAGE_READ_FILE = 0;
|
||||||
|
// const int LOAD_STEP_STAGE_GET_SOLID = 1;
|
||||||
|
// const int LOAD_STEP_STAGE_GET_MESH = 2;
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
struct NamedSolid {
|
||||||
|
NamedSolid(const TopoDS_Shape& s,
|
||||||
|
const std::string& n) : solid{s}, name{n} {}
|
||||||
|
const TopoDS_Shape solid;
|
||||||
|
const std::string name;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void getNamedSolids(const TopLoc_Location& location, const Handle(XCAFDoc_ShapeTool) shapeTool,
|
||||||
|
const TDF_Label label, std::vector<NamedSolid>& namedSolids)
|
||||||
|
{
|
||||||
|
TDF_Label referredLabel{label};
|
||||||
|
if (shapeTool->IsReference(label))
|
||||||
|
shapeTool->GetReferredShape(label, referredLabel);
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
Handle(TDataStd_Name) shapeName;
|
||||||
|
if (referredLabel.FindAttribute(TDataStd_Name::GetID(), shapeName))
|
||||||
|
name = TCollection_AsciiString(shapeName->Get()).ToCString();
|
||||||
|
|
||||||
|
TopLoc_Location localLocation = location * shapeTool->GetLocation(label);
|
||||||
|
TDF_LabelSequence components;
|
||||||
|
if (shapeTool->GetComponents(referredLabel, components)) {
|
||||||
|
for (Standard_Integer compIndex = 1; compIndex <= components.Length(); ++compIndex) {
|
||||||
|
getNamedSolids(localLocation, shapeTool, components.Value(compIndex), namedSolids);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TopoDS_Shape shape;
|
||||||
|
shapeTool->GetShape(referredLabel, shape);
|
||||||
|
TopAbs_ShapeEnum shape_type = shape.ShapeType();
|
||||||
|
BRepBuilderAPI_Transform transform(shape, localLocation, Standard_True);
|
||||||
|
switch (shape_type) {
|
||||||
|
case TopAbs_COMPOUND:
|
||||||
|
namedSolids.emplace_back(TopoDS::Compound(transform.Shape()), name);
|
||||||
|
break;
|
||||||
|
case TopAbs_COMPSOLID:
|
||||||
|
namedSolids.emplace_back(TopoDS::CompSolid(transform.Shape()), name);
|
||||||
|
break;
|
||||||
|
case TopAbs_SOLID:
|
||||||
|
namedSolids.emplace_back(TopoDS::Solid(transform.Shape()), name);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" OCCTWRAPPER_EXPORT bool load_step_internal(const char *path, OCCTResult* res /*BBS:, ImportStepProgressFn proFn*/)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
//bool cb_cancel = false;
|
||||||
|
//if (proFn) {
|
||||||
|
// proFn(LOAD_STEP_STAGE_READ_FILE, 0, 1, cb_cancel);
|
||||||
|
// if (cb_cancel)
|
||||||
|
// return false;
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<NamedSolid> namedSolids;
|
||||||
|
Handle(TDocStd_Document) document;
|
||||||
|
Handle(XCAFApp_Application) application = XCAFApp_Application::GetApplication();
|
||||||
|
application->NewDocument(path, document);
|
||||||
|
STEPCAFControl_Reader reader;
|
||||||
|
reader.SetNameMode(true);
|
||||||
|
//BBS: Todo, read file is slow which cause the progress_bar no update and gui no response
|
||||||
|
IFSelect_ReturnStatus stat = reader.ReadFile(path);
|
||||||
|
if (stat != IFSelect_RetDone || !reader.Transfer(document)) {
|
||||||
|
application->Close(document);
|
||||||
|
res->error_str = std::string{"Could not read '"} + path + "'";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Handle(XCAFDoc_ShapeTool) shapeTool = XCAFDoc_DocumentTool::ShapeTool(document->Main());
|
||||||
|
TDF_LabelSequence topLevelShapes;
|
||||||
|
shapeTool->GetFreeShapes(topLevelShapes);
|
||||||
|
|
||||||
|
Standard_Integer topShapeLength = topLevelShapes.Length() + 1;
|
||||||
|
for (Standard_Integer iLabel = 1; iLabel < topShapeLength; ++iLabel) {
|
||||||
|
//if (proFn) {
|
||||||
|
// proFn(LOAD_STEP_STAGE_GET_SOLID, iLabel, topShapeLength, cb_cancel);
|
||||||
|
// if (cb_cancel) {
|
||||||
|
// shapeTool.reset(nullptr);
|
||||||
|
// application->Close(document);
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
getNamedSolids(TopLoc_Location{}, shapeTool, topLevelShapes.Value(iLabel), namedSolids);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Now the object name. Set it to filename without suffix.
|
||||||
|
// This will later be changed if only one volume is loaded.
|
||||||
|
const char *last_slash = strrchr(path, DIR_SEPARATOR);
|
||||||
|
std::string obj_name((last_slash == nullptr) ? path : last_slash + 1);
|
||||||
|
res->object_name = obj_name;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < namedSolids.size(); ++i) {
|
||||||
|
//BBS:if (proFn) {
|
||||||
|
// proFn(LOAD_STEP_STAGE_GET_MESH, i, namedSolids.size(), cb_cancel);
|
||||||
|
// if (cb_cancel) {
|
||||||
|
// model->delete_object(new_object);
|
||||||
|
// shapeTool.reset(nullptr);
|
||||||
|
// application->Close(document);
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
res->volumes.emplace_back();
|
||||||
|
auto& vertices = res->volumes.back().vertices;
|
||||||
|
auto& indices = res->volumes.back().indices;
|
||||||
|
|
||||||
|
BRepMesh_IncrementalMesh mesh(namedSolids[i].solid, STEP_TRANS_CHORD_ERROR, false, STEP_TRANS_ANGLE_RES, true);
|
||||||
|
|
||||||
|
for (TopExp_Explorer anExpSF(namedSolids[i].solid, TopAbs_FACE); anExpSF.More(); anExpSF.Next()) {
|
||||||
|
const int aNodeOffset = int(vertices.size());
|
||||||
|
const TopoDS_Shape& aFace = anExpSF.Current();
|
||||||
|
TopLoc_Location aLoc;
|
||||||
|
Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation(TopoDS::Face(aFace), aLoc);
|
||||||
|
if (aTriangulation.IsNull())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// First copy vertices (will create duplicates).
|
||||||
|
gp_Trsf aTrsf = aLoc.Transformation();
|
||||||
|
for (Standard_Integer aNodeIter = 1; aNodeIter <= aTriangulation->NbNodes(); ++aNodeIter) {
|
||||||
|
gp_Pnt aPnt = aTriangulation->Node(aNodeIter);
|
||||||
|
aPnt.Transform(aTrsf);
|
||||||
|
vertices.push_back({float(aPnt.X()), float(aPnt.Y()), float(aPnt.Z())});
|
||||||
|
}
|
||||||
|
// Now the indices.
|
||||||
|
const TopAbs_Orientation anOrientation = anExpSF.Current().Orientation();
|
||||||
|
for (Standard_Integer aTriIter = 1; aTriIter <= aTriangulation->NbTriangles(); ++aTriIter) {
|
||||||
|
Poly_Triangle aTri = aTriangulation->Triangle(aTriIter);
|
||||||
|
|
||||||
|
Standard_Integer anId[3];
|
||||||
|
aTri.Get(anId[0], anId[1], anId[2]);
|
||||||
|
if (anOrientation == TopAbs_REVERSED)
|
||||||
|
std::swap(anId[1], anId[2]);
|
||||||
|
|
||||||
|
// Account for the vertices we already have from previous faces.
|
||||||
|
// anId is 1-based index !
|
||||||
|
indices.push_back({anId[0] - 1 + aNodeOffset,
|
||||||
|
anId[1] - 1 + aNodeOffset,
|
||||||
|
anId[2] - 1 + aNodeOffset});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res->volumes.back().volume_name = namedSolids[i].name;
|
||||||
|
|
||||||
|
if (vertices.empty())
|
||||||
|
res->volumes.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
shapeTool.reset(nullptr);
|
||||||
|
application->Close(document);
|
||||||
|
|
||||||
|
if (res->volumes.empty())
|
||||||
|
return false;
|
||||||
|
} catch (const std::exception& ex) {
|
||||||
|
res->error_str = ex.what();
|
||||||
|
return false;
|
||||||
|
} catch (...) {
|
||||||
|
res->error_str = "An exception was thrown in load_step_internal.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // namespace Slic3r
|
27
src/occt_wrapper/OCCTWrapper.hpp
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
#ifndef occtwrapper_OCCTWrapper_hpp_
|
||||||
|
#define occtwrapper_OCCTWrapper_hpp_
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
struct OCCTVolume {
|
||||||
|
std::string volume_name;
|
||||||
|
std::vector<std::array<float, 3>> vertices;
|
||||||
|
std::vector<std::array<int, 3>> indices;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OCCTResult {
|
||||||
|
std::string error_str;
|
||||||
|
std::string object_name;
|
||||||
|
std::vector<OCCTVolume> volumes;
|
||||||
|
};
|
||||||
|
|
||||||
|
using LoadStepFn = bool (*)(const char *path, OCCTResult* occt_result);
|
||||||
|
|
||||||
|
}; // namespace Slic3r
|
||||||
|
|
||||||
|
#endif // occtwrapper_OCCTWrapper_hpp_
|
@ -493,51 +493,51 @@ wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_
|
|||||||
|
|
||||||
return this->insert_raw_rgba(bitmap_key, width, height, data.data(), grayscale);
|
return this->insert_raw_rgba(bitmap_key, width, height, data.data(), grayscale);
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
//we make scaled solid bitmaps only for the cases, when its will be used with scaled SVG icon in one output bitmap
|
//we make scaled solid bitmaps only for the cases, when its will be used with scaled SVG icon in one output bitmap
|
||||||
wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, bool suppress_scaling/* = false* /, size_t border_width /*= 0* /, bool dark_mode/* = false* /)
|
//wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, bool suppress_scaling/* = false* /, size_t border_width /*= 0* /, bool dark_mode/* = false* /)
|
||||||
{
|
//{
|
||||||
double scale = suppress_scaling ? 1.0f : m_scale;
|
// double scale = suppress_scaling ? 1.0f : m_scale;
|
||||||
width *= scale;
|
// width *= scale;
|
||||||
height *= scale;
|
// height *= scale;
|
||||||
|
//
|
||||||
|
// wxImage image(width, height);
|
||||||
|
// image.InitAlpha();
|
||||||
|
// unsigned char* imgdata = image.GetData();
|
||||||
|
// unsigned char* imgalpha = image.GetAlpha();
|
||||||
|
// for (size_t i = 0; i < width * height; ++ i) {
|
||||||
|
// *imgdata ++ = r;
|
||||||
|
// *imgdata ++ = g;
|
||||||
|
// *imgdata ++ = b;
|
||||||
|
// *imgalpha ++ = transparency;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Add border, make white/light spools easier to see
|
||||||
|
// if (border_width > 0) {
|
||||||
|
//
|
||||||
|
// // Restrict to width of image
|
||||||
|
// if (border_width > height) border_width = height - 1;
|
||||||
|
// if (border_width > width) border_width = width - 1;
|
||||||
|
//
|
||||||
|
// auto px_data = (uint8_t*)image.GetData();
|
||||||
|
// auto a_data = (uint8_t*)image.GetAlpha();
|
||||||
|
//
|
||||||
|
// for (size_t x = 0; x < width; ++x) {
|
||||||
|
// for (size_t y = 0; y < height; ++y) {
|
||||||
|
// if (x < border_width || y < border_width ||
|
||||||
|
// x >= (width - border_width) || y >= (height - border_width)) {
|
||||||
|
// const size_t idx = (x + y * width);
|
||||||
|
// const size_t idx_rgb = (x + y * width) * 3;
|
||||||
|
// px_data[idx_rgb] = px_data[idx_rgb + 1] = px_data[idx_rgb + 2] = dark_mode ? 245u : 110u;
|
||||||
|
// a_data[idx] = 255u;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return wxImage_to_wxBitmap_with_alpha(std::move(image), scale);
|
||||||
|
//}
|
||||||
|
|
||||||
wxImage image(width, height);
|
|
||||||
image.InitAlpha();
|
|
||||||
unsigned char* imgdata = image.GetData();
|
|
||||||
unsigned char* imgalpha = image.GetAlpha();
|
|
||||||
for (size_t i = 0; i < width * height; ++ i) {
|
|
||||||
*imgdata ++ = r;
|
|
||||||
*imgdata ++ = g;
|
|
||||||
*imgdata ++ = b;
|
|
||||||
*imgalpha ++ = transparency;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add border, make white/light spools easier to see
|
|
||||||
if (border_width > 0) {
|
|
||||||
|
|
||||||
// Restrict to width of image
|
|
||||||
if (border_width > height) border_width = height - 1;
|
|
||||||
if (border_width > width) border_width = width - 1;
|
|
||||||
|
|
||||||
auto px_data = (uint8_t*)image.GetData();
|
|
||||||
auto a_data = (uint8_t*)image.GetAlpha();
|
|
||||||
|
|
||||||
for (size_t x = 0; x < width; ++x) {
|
|
||||||
for (size_t y = 0; y < height; ++y) {
|
|
||||||
if (x < border_width || y < border_width ||
|
|
||||||
x >= (width - border_width) || y >= (height - border_width)) {
|
|
||||||
const size_t idx = (x + y * width);
|
|
||||||
const size_t idx_rgb = (x + y * width) * 3;
|
|
||||||
px_data[idx_rgb] = px_data[idx_rgb + 1] = px_data[idx_rgb + 2] = dark_mode ? 245u : 110u;
|
|
||||||
a_data[idx] = 255u;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return wxImage_to_wxBitmap_with_alpha(std::move(image), scale);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
//we make scaled solid bitmaps only for the cases, when its will be used with scaled SVG icon in one output bitmap
|
//we make scaled solid bitmaps only for the cases, when its will be used with scaled SVG icon in one output bitmap
|
||||||
wxBitmapBundle BitmapCache::mksolid(size_t width_in, size_t height_in, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, size_t border_width /*= 0*/, bool dark_mode/* = false*/)
|
wxBitmapBundle BitmapCache::mksolid(size_t width_in, size_t height_in, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, size_t border_width /*= 0*/, bool dark_mode/* = false*/)
|
||||||
{
|
{
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
#include <set>
|
#include <set>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
#include <boost/nowide/convert.hpp>
|
||||||
|
|
||||||
#include <wx/sizer.h>
|
#include <wx/sizer.h>
|
||||||
#include <wx/button.h>
|
#include <wx/button.h>
|
||||||
#include <wx/listctrl.h>
|
#include <wx/listctrl.h>
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
#ifndef slic3r_BonjourDialog_hpp_
|
#ifndef slic3r_BonjourDialog_hpp_
|
||||||
#define slic3r_BonjourDialog_hpp_
|
#define slic3r_BonjourDialog_hpp_
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include <boost/asio/ip/address.hpp>
|
||||||
|
|
||||||
#include <wx/dialog.h>
|
#include <wx/dialog.h>
|
||||||
#include <wx/string.h>
|
#include <wx/string.h>
|
||||||
|
|
||||||
|
@ -323,8 +323,6 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
|
|||||||
toggle_field("wall_transition_filter_deviation", have_arachne);
|
toggle_field("wall_transition_filter_deviation", have_arachne);
|
||||||
toggle_field("wall_transition_angle", have_arachne);
|
toggle_field("wall_transition_angle", have_arachne);
|
||||||
toggle_field("wall_distribution_count", have_arachne);
|
toggle_field("wall_distribution_count", have_arachne);
|
||||||
toggle_field("wall_split_middle_threshold", have_arachne);
|
|
||||||
toggle_field("wall_add_middle_threshold", have_arachne);
|
|
||||||
toggle_field("min_feature_size", have_arachne);
|
toggle_field("min_feature_size", have_arachne);
|
||||||
toggle_field("min_bead_width", have_arachne);
|
toggle_field("min_bead_width", have_arachne);
|
||||||
toggle_field("thin_walls", !have_arachne);
|
toggle_field("thin_walls", !have_arachne);
|
||||||
|
@ -123,13 +123,13 @@ public:
|
|||||||
{
|
{
|
||||||
wxASSERT(bitmap.IsOk());
|
wxASSERT(bitmap.IsOk());
|
||||||
|
|
||||||
int init_dpi = get_dpi_for_window(this);
|
// int init_dpi = get_dpi_for_window(this);
|
||||||
this->SetPosition(pos);
|
this->SetPosition(pos);
|
||||||
// The size of the SplashScreen can be hanged after its moving to another display
|
// The size of the SplashScreen can be hanged after its moving to another display
|
||||||
// So, update it from a bitmap size
|
// So, update it from a bitmap size
|
||||||
this->SetClientSize(bitmap.GetWidth(), bitmap.GetHeight());
|
this->SetClientSize(bitmap.GetWidth(), bitmap.GetHeight());
|
||||||
this->CenterOnScreen();
|
this->CenterOnScreen();
|
||||||
int new_dpi = get_dpi_for_window(this);
|
// int new_dpi = get_dpi_for_window(this);
|
||||||
|
|
||||||
// m_scale = (float)(new_dpi) / (float)(init_dpi);
|
// m_scale = (float)(new_dpi) / (float)(init_dpi);
|
||||||
m_main_bitmap = bitmap;
|
m_main_bitmap = bitmap;
|
||||||
@ -457,14 +457,17 @@ struct FileWildcards {
|
|||||||
std::vector<std::string_view> file_extensions;
|
std::vector<std::string_view> file_extensions;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static const FileWildcards file_wildcards_by_type[FT_SIZE] = {
|
static const FileWildcards file_wildcards_by_type[FT_SIZE] = {
|
||||||
/* FT_STL */ { "STL files"sv, { ".stl"sv } },
|
/* FT_STL */ { "STL files"sv, { ".stl"sv } },
|
||||||
/* FT_OBJ */ { "OBJ files"sv, { ".obj"sv } },
|
/* FT_OBJ */ { "OBJ files"sv, { ".obj"sv } },
|
||||||
/* FT_OBJECT */ { "Object files"sv, { ".stl"sv, ".obj"sv } },
|
/* FT_OBJECT */ { "Object files"sv, { ".stl"sv, ".obj"sv } },
|
||||||
|
/* FT_STEP */ { "STEP files"sv, { ".stp"sv, ".step"sv } },
|
||||||
/* FT_AMF */ { "AMF files"sv, { ".amf"sv, ".zip.amf"sv, ".xml"sv } },
|
/* FT_AMF */ { "AMF files"sv, { ".amf"sv, ".zip.amf"sv, ".xml"sv } },
|
||||||
/* FT_3MF */ { "3MF files"sv, { ".3mf"sv } },
|
/* FT_3MF */ { "3MF files"sv, { ".3mf"sv } },
|
||||||
/* FT_GCODE */ { "G-code files"sv, { ".gcode"sv, ".gco"sv, ".g"sv, ".ngc"sv } },
|
/* FT_GCODE */ { "G-code files"sv, { ".gcode"sv, ".gco"sv, ".g"sv, ".ngc"sv } },
|
||||||
/* FT_MODEL */ { "Known files"sv, { ".stl"sv, ".obj"sv, ".3mf"sv, ".amf"sv, ".zip.amf"sv, ".xml"sv } },
|
/* FT_MODEL */ { "Known files"sv, { ".stl"sv, ".obj"sv, ".3mf"sv, ".amf"sv, ".zip.amf"sv, ".xml"sv, ".step"sv, ".stp"sv } },
|
||||||
/* FT_PROJECT */ { "Project files"sv, { ".3mf"sv, ".amf"sv, ".zip.amf"sv } },
|
/* FT_PROJECT */ { "Project files"sv, { ".3mf"sv, ".amf"sv, ".zip.amf"sv } },
|
||||||
/* FT_GALLERY */ { "Known files"sv, { ".stl"sv, ".obj"sv } },
|
/* FT_GALLERY */ { "Known files"sv, { ".stl"sv, ".obj"sv } },
|
||||||
|
|
||||||
@ -893,9 +896,9 @@ static boost::optional<Semver> parse_semver_from_ini(std::string path)
|
|||||||
void GUI_App::init_app_config()
|
void GUI_App::init_app_config()
|
||||||
{
|
{
|
||||||
// Profiles for the alpha are stored into the PrusaSlicer-alpha directory to not mix with the current release.
|
// Profiles for the alpha are stored into the PrusaSlicer-alpha directory to not mix with the current release.
|
||||||
// SetAppName(SLIC3R_APP_KEY);
|
// SetAppName(SLIC3R_APP_KEY);
|
||||||
SetAppName(SLIC3R_APP_KEY "-alpha");
|
SetAppName(SLIC3R_APP_KEY "-alpha");
|
||||||
// SetAppName(SLIC3R_APP_KEY "-beta");
|
// SetAppName(SLIC3R_APP_KEY "-beta");
|
||||||
|
|
||||||
|
|
||||||
// SetAppDisplayName(SLIC3R_APP_NAME);
|
// SetAppDisplayName(SLIC3R_APP_NAME);
|
||||||
@ -1893,7 +1896,7 @@ void GUI_App::import_model(wxWindow *parent, wxArrayString& input_files) const
|
|||||||
{
|
{
|
||||||
input_files.Clear();
|
input_files.Clear();
|
||||||
wxFileDialog dialog(parent ? parent : GetTopWindow(),
|
wxFileDialog dialog(parent ? parent : GetTopWindow(),
|
||||||
_L("Choose one or more files (STL/OBJ/AMF/3MF/PRUSA):"),
|
_L("Choose one or more files (STL/OBJ/AMF/3MF/PRUSA/STEP):"),
|
||||||
from_u8(app_config->get_last_dir()), "",
|
from_u8(app_config->get_last_dir()), "",
|
||||||
file_wildcards(FT_MODEL), wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST);
|
file_wildcards(FT_MODEL), wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST);
|
||||||
|
|
||||||
|
@ -55,6 +55,7 @@ enum FileType
|
|||||||
FT_STL,
|
FT_STL,
|
||||||
FT_OBJ,
|
FT_OBJ,
|
||||||
FT_OBJECT,
|
FT_OBJECT,
|
||||||
|
FT_STEP,
|
||||||
FT_AMF,
|
FT_AMF,
|
||||||
FT_3MF,
|
FT_3MF,
|
||||||
FT_GCODE,
|
FT_GCODE,
|
||||||
|
@ -2106,6 +2106,8 @@ void ObjectList::split()
|
|||||||
Expand(parent);
|
Expand(parent);
|
||||||
|
|
||||||
changed_object(obj_idx);
|
changed_object(obj_idx);
|
||||||
|
// update printable state for new volumes on canvas3D
|
||||||
|
wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_object(obj_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectList::merge(bool to_multipart_object)
|
void ObjectList::merge(bool to_multipart_object)
|
||||||
@ -2219,8 +2221,12 @@ void ObjectList::merge(bool to_multipart_object)
|
|||||||
const Vec3d mirror = transformation.get_mirror();
|
const Vec3d mirror = transformation.get_mirror();
|
||||||
const Vec3d rotation = transformation.get_rotation();
|
const Vec3d rotation = transformation.get_rotation();
|
||||||
|
|
||||||
if (object->id() == (*m_objects)[obj_idxs.front()]->id())
|
if (object->id() == (*m_objects)[obj_idxs.front()]->id()) {
|
||||||
new_object->add_instance();
|
new_object->add_instance();
|
||||||
|
new_object->instances[0]->printable = false;
|
||||||
|
}
|
||||||
|
new_object->instances[0]->printable |= object->instances[0]->printable;
|
||||||
|
|
||||||
const Transform3d& volume_offset_correction = transformation.get_matrix();
|
const Transform3d& volume_offset_correction = transformation.get_matrix();
|
||||||
|
|
||||||
// merge volumes
|
// merge volumes
|
||||||
@ -2285,6 +2291,9 @@ void ObjectList::merge(bool to_multipart_object)
|
|||||||
add_object_to_list(m_objects->size() - 1);
|
add_object_to_list(m_objects->size() - 1);
|
||||||
select_item(m_objects_model->GetItemById(m_objects->size() - 1));
|
select_item(m_objects_model->GetItemById(m_objects->size() - 1));
|
||||||
update_selections_on_canvas();
|
update_selections_on_canvas();
|
||||||
|
|
||||||
|
// update printable state for new volumes on canvas3D
|
||||||
|
wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_object(int(model->objects.size()) - 1);
|
||||||
}
|
}
|
||||||
// merge all parts to the one single object
|
// merge all parts to the one single object
|
||||||
// all part's settings will be lost
|
// all part's settings will be lost
|
||||||
|
@ -504,6 +504,8 @@ void GLGizmoBase::set_dirty() {
|
|||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void GLGizmoBase::render_input_window(float x, float y, float bottom_limit)
|
void GLGizmoBase::render_input_window(float x, float y, float bottom_limit)
|
||||||
{
|
{
|
||||||
on_render_input_window(x, y, bottom_limit);
|
on_render_input_window(x, y, bottom_limit);
|
||||||
@ -532,3 +534,4 @@ std::string GLGizmoBase::get_name(bool include_shortcut) const
|
|||||||
|
|
||||||
} // namespace GUI
|
} // namespace GUI
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
||||||
|
@ -212,8 +212,8 @@ std::string GLGizmoCut3D::get_tooltip() const
|
|||||||
}
|
}
|
||||||
if (tooltip.empty() && (m_hover_id == X || m_hover_id == Y)) {
|
if (tooltip.empty() && (m_hover_id == X || m_hover_id == Y)) {
|
||||||
std::string axis = m_hover_id == X ? "X" : "Y";
|
std::string axis = m_hover_id == X ? "X" : "Y";
|
||||||
// return axis + ": " + format(float(Geometry::rad2deg(Geometry::Transformation(m_rotation_m).get_rotation()[m_hover_id])), 1) + _u8L("°");
|
// return axis + ": " + format(float(Geometry::rad2deg(Geometry::Transformation(m_rotation_m).get_rotation()[m_hover_id])), 1) + _u8L("°");
|
||||||
return axis + ": " + format(float(Geometry::rad2deg(m_angle)), 1) + _u8L("°");
|
return axis + ": " + format(float(Geometry::rad2deg(m_angle)), 1) + _u8L("°");
|
||||||
}
|
}
|
||||||
|
|
||||||
return tooltip;
|
return tooltip;
|
||||||
|
@ -80,7 +80,7 @@ void KBShortcutsDialog::fill_shortcuts()
|
|||||||
{ ctrl + alt + "S", L("Save project as (3mf)") },
|
{ ctrl + alt + "S", L("Save project as (3mf)") },
|
||||||
{ ctrl + "R", L("(Re)slice") },
|
{ ctrl + "R", L("(Re)slice") },
|
||||||
// File>Import
|
// File>Import
|
||||||
{ ctrl + "I", L("Import STL/OBJ/AMF/3MF without config, keep plater") },
|
{ ctrl + "I", L("Import STL/OBJ/AMF/3MF/STEP without config, keep plater") },
|
||||||
{ ctrl + "L", L("Import Config from ini/amf/3mf/gcode") },
|
{ ctrl + "L", L("Import Config from ini/amf/3mf/gcode") },
|
||||||
{ ctrl + alt + "L", L("Load Config from ini/amf/3mf/gcode and merge") },
|
{ ctrl + alt + "L", L("Load Config from ini/amf/3mf/gcode and merge") },
|
||||||
// File>Export
|
// File>Export
|
||||||
|
@ -1212,7 +1212,7 @@ void MainFrame::init_menubar_as_editor()
|
|||||||
fileMenu->AppendSeparator();
|
fileMenu->AppendSeparator();
|
||||||
|
|
||||||
wxMenu* import_menu = new wxMenu();
|
wxMenu* import_menu = new wxMenu();
|
||||||
append_menu_item(import_menu, wxID_ANY, _L("Import STL/OBJ/AM&F/3MF") + dots + "\tCtrl+I", _L("Load a model"),
|
append_menu_item(import_menu, wxID_ANY, _L("Import STL/OBJ/AM&F/3MF/STEP") + dots + "\tCtrl+I", _L("Load a model"),
|
||||||
[this](wxCommandEvent&) { if (m_plater) m_plater->add_model(); }, "import_plater", nullptr,
|
[this](wxCommandEvent&) { if (m_plater) m_plater->add_model(); }, "import_plater", nullptr,
|
||||||
[this](){return m_plater != nullptr; }, this);
|
[this](){return m_plater != nullptr; }, this);
|
||||||
|
|
||||||
|
@ -2401,9 +2401,20 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||||||
|
|
||||||
const auto loading = _L("Loading") + dots;
|
const auto loading = _L("Loading") + dots;
|
||||||
|
|
||||||
// Create wxProgressDialog on heap, see the linux ifdef below.
|
// The situation with wxProgressDialog is quite interesting here.
|
||||||
auto progress_dlg = new wxProgressDialog(loading, "", 100, find_toplevel_parent(q), wxPD_AUTO_HIDE);
|
// On Linux (only), there are issues when FDM/SLA is switched during project file loading (disabling of controls,
|
||||||
|
// see a comment below). This can be bypassed by creating the wxProgressDialog on heap and destroying it
|
||||||
|
// when loading a project file. However, creating the dialog on heap causes issues on macOS, where it does not
|
||||||
|
// appear at all. Therefore, we create the dialog on stack on Win and macOS, and on heap on Linux, which
|
||||||
|
// is the only system that needed the workarounds in the first place.
|
||||||
|
#ifdef __linux__
|
||||||
|
auto progress_dlg = new wxProgressDialog(loading, "", 100, find_toplevel_parent(q), wxPD_APP_MODAL | wxPD_AUTO_HIDE);
|
||||||
Slic3r::ScopeGuard([&progress_dlg](){ if (progress_dlg) progress_dlg->Destroy(); progress_dlg = nullptr; });
|
Slic3r::ScopeGuard([&progress_dlg](){ if (progress_dlg) progress_dlg->Destroy(); progress_dlg = nullptr; });
|
||||||
|
#else
|
||||||
|
wxProgressDialog progress_dlg_stack(loading, "", 100, find_toplevel_parent(q), wxPD_APP_MODAL | wxPD_AUTO_HIDE);
|
||||||
|
wxProgressDialog* progress_dlg = &progress_dlg_stack;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
wxBusyCursor busy;
|
wxBusyCursor busy;
|
||||||
|
|
||||||
@ -3136,6 +3147,8 @@ void Plater::priv::split_object()
|
|||||||
for (size_t idx : idxs)
|
for (size_t idx : idxs)
|
||||||
{
|
{
|
||||||
get_selection().add_object((unsigned int)idx, false);
|
get_selection().add_object((unsigned int)idx, false);
|
||||||
|
// update printable state for new volumes on canvas3D
|
||||||
|
q->canvas3D()->update_instance_printable_state_for_object(idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5571,7 +5584,7 @@ void ProjectDropDialog::on_dpi_changed(const wxRect& suggested_rect)
|
|||||||
|
|
||||||
bool Plater::load_files(const wxArrayString& filenames)
|
bool Plater::load_files(const wxArrayString& filenames)
|
||||||
{
|
{
|
||||||
const std::regex pattern_drop(".*[.](stl|obj|amf|3mf|prusa)", std::regex::icase);
|
const std::regex pattern_drop(".*[.](stl|obj|amf|3mf|prusa|step|stp)", std::regex::icase);
|
||||||
const std::regex pattern_gcode_drop(".*[.](gcode|g)", std::regex::icase);
|
const std::regex pattern_gcode_drop(".*[.](gcode|g)", std::regex::icase);
|
||||||
|
|
||||||
std::vector<fs::path> paths;
|
std::vector<fs::path> paths;
|
||||||
|
@ -434,7 +434,7 @@ static std::string generate_system_info_json()
|
|||||||
|
|
||||||
pt::ptree hw_node;
|
pt::ptree hw_node;
|
||||||
{
|
{
|
||||||
hw_node.put("ArchName", wxPlatformInfo::Get().GetArchName());
|
hw_node.put("ArchName", wxPlatformInfo::Get().GetBitnessName());
|
||||||
size_t num = std::round(Slic3r::total_physical_memory()/107374100.);
|
size_t num = std::round(Slic3r::total_physical_memory()/107374100.);
|
||||||
hw_node.put("RAM_GiB", std::to_string(num / 10) + "." + std::to_string(num % 10));
|
hw_node.put("RAM_GiB", std::to_string(num / 10) + "." + std::to_string(num % 10));
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ std::string get_main_info(bool format_as_html)
|
|||||||
|
|
||||||
out << line_end;
|
out << line_end;
|
||||||
out << b_start << "Operating System: " << b_end << wxPlatformInfo::Get().GetOperatingSystemFamilyName() << line_end;
|
out << b_start << "Operating System: " << b_end << wxPlatformInfo::Get().GetOperatingSystemFamilyName() << line_end;
|
||||||
out << b_start << "System Architecture: " << b_end << wxPlatformInfo::Get().GetArchName() << line_end;
|
out << b_start << "System Architecture: " << b_end << wxPlatformInfo::Get().GetBitnessName() << line_end;
|
||||||
out << b_start <<
|
out << b_start <<
|
||||||
#if defined _WIN32
|
#if defined _WIN32
|
||||||
"Windows Version: "
|
"Windows Version: "
|
||||||
|